C++ static 类成员
节选自《C++ Primer》,还是书上写的最清楚。
?
?
?
对于特定类类型的全体对象而言,访问一个全局对象有时是必要的。也许,在程序的任意点需要统计已创建的特定类类型对象的数量;或者,全局对象可能是指向类的错误处理例程的一个指针;或者,它是指向类类型对象的内在自由存储区的一个指针。
?
然而,全局对象会破坏封装:对象需要支持特定类抽象的实现。如果对象是全局的,一般的用户代码就可以修改这个值。类可以定义类?静态成员,而不是定义一个可普遍访问的全局对象。
?
通常,非?static?数据成员存在于类类型的每个对象中。不像普通的数据成员,static?数据成员独立于该类的任意对象而存在;每个?static?数据成员是与类关联的对象,并不与该类的对象相关联。
?
正如类可以定义共享的?static?数据成员一样,类也可以定义?static?成员函数。static?成员函数没有?this?形参,它可以直接访问所属类的?static?成员,但不能直接使用非?static?成员。
使用?static?成员而不是全局对象有三个优点。
?成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突。
可以实施封装。static?成员可以是私有成员,而全局对象不可以。
通过阅读程序容易看出?static?成员是与特定类关联的。这种可见性可清晰地显示程序员的意图。
在成员声明前加上关键字?static?将成员设为?static。static?成员遵循正常的公有/私有访问规则。
?
例如,考虑一个简单的表示银行账户的类。每个账户具有余额和拥有者,并且按月获得利息,但应用于每个账户的利率总是相同的。可以按下面的这样编写这个类
class Account { public: // interface functions here void applyint() { amount += amount * interestRate; } static double rate() { return interestRate; } static void rate(double); // sets a new rate private: std::string owner; double amount; static double interestRate; static double initRate(); };这个类的每个对象具有两个数据成员:owner?和?amount。对象没有与?static?数据成员对应的数据成员,但是,存在一个单独的interestRate?对象,由?Account?类型的全体对象共享。
可以通过作用域操作符从类直接调用?static?成员,或者通过对象、引用或指向该类类型对象的指针间接调用。
Account ac1; Account *ac2 = &ac1; // equivalent ways to call the static member rate function double rate; rate = ac1.rate(); // through an Account object or reference rate = ac2->rate(); // through a pointer to an Account object rate = Account::rate(); // directly from the class using the scope operator
像使用其他成员一样,类成员函数可以不用作用域操作符来引用类的?static?成员:
class Account { public: // interface functions here void applyint() { amount += amount * interestRate; } };?
Account?类有两个名为?rate?的?static?成员函数,其中一个定义在类的内部。当我们在类的外部定义?static?成员时,无须重复指定?static?保留字,该保留字只出现在类定义体内部的声明处:
void Account::rate(double newRate) { interestRate = newRate; }static?成员是类的组成部分但不是任何对象的组成部分,因此,static?成员函数没有?this?指针。通过使用非?static?成员显式或隐式地引用?this?是一个编译时错误。
?
因为?static?成员不是任何对象的组成部分,所以?static?成员函数不能被声明为?const。毕竟,将成员函数声明为?const?就是承诺不会修改该函数所属的对象。最后,static?成员函数也不能被声明为虚函数。我们将在第 15.2.4 节学习虚函数。
static?数据成员可以声明为任意类型,可以是常量、引用、数组、类类型,等等。
static?数据成员必须在类定义体的外部定义(正好一次)。不像普通数据成员,static?成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。
?
保证对象正好定义一次的最好办法,就是将?static?数据成员的定义放在包含类非内联成员函数定义的文件中。
定义?static?数据成员的方式与定义其他类成员和变量的方式相同:先指定类型名,接着是成员的完全限定名。
?
可以定义如下?interestRate:
// define and initialize static class member double Account::interestRate = initRate();
这个语句定义名为?interestRate?的?static?对象,它是类?Account?的成员,为?double?型。像其他成员定义一样,一旦成员名出现,static?成员的就是在类作用域中。因此,我们可以没有限定地直接使用名为?initRate?的?static?成员函数,作为interestRate?初始化式。注意,尽管?initRate?是私有的,我们仍然可以使用该函数来初始化?interestRate。像任意的其他成员定义一样,interestRate?的定义是在类的作用域中,因此可以访问该类的私有成员。
像使用任意的类成员一样,在类定义体外部引用类的?static?成员时,必须指定成员是在哪个类中定义的。然而,static?关键字只能用于类定义体内部的声明中,定义不能标示为?static。
?
一般而言,类的?static?成员,像普通数据成员一样,不能在类的定义体中初始化。相反,static?数据成员通常在定义时才初始化。
?
这个规则的一个例外是,只要初始化式是一个常量表达式,整型?const static?数据成员就可以在类的定义体中进行初始化:
class Account { public: static double rate() { return interestRate; } static void rate(double); // sets a new rate private: static const int period = 30; // interest posted every 30 days double daily_tbl[period]; // ok: period is constant expression };?
用常量值初始化的整型?const static?数据成员是一个常量表达式。同样地,它可以用在任何需要常量表达式的地方,例如指定数组成员?daily_tbl?的维。
const static?数据成员在类的定义体中初始化时,该数据成员仍必须在类的定义体之外进行定义。
在类内部提供初始化式时,成员的定义不必再指定初始值:
// definition of static member with no initializer; // the initial value is specified inside the class definition const int Account::period;
普通成员都是给定类的每个对象的组成部分。static?成员独立于任何对象而存在,不是类类型对象的组成部分。因为?static数据成员不是任何对象的组成部分,所以它们的使用方式对于非?static?数据成员而言是不合法的。
?
例如,static?数据成员的类型可以是该成员所属的类类型。非?static?成员被限定声明为其自身类对象的指针或引用:
class Bar { public: // ... private: static Bar mem1; // ok Bar *mem2; // ok Bar mem3; // error };类似地,static?数据成员可用作默认实参:
class Screen { public: // bkground refers to the static member // declared later in the class definition Screen& clear(char = bkground); private: static const char bkground = '#'; };非?static?数据成员不能用作默认实参,因为它的值不能独立于所属的对象而使用。使用非?static?数据成员作默认实参,将无法提供对象以获取该成员的值,因而是错误的。
?
ps:???以下关于静态成员变量
1.初始化static成员变量不能安排在类的构造函数中,因为构造函数可能一再被调用,而变量的初值却只应该设定一次。也不要把初始化安排在头文件中,因为它可能会被包含在许多地方。而应该放在main函数之中,或全域函数中,或者任何函数之外。
2.静态成员变量(函数)在类外定义时,注意不要写static,初始化时候 [类型名] [类名]::[变量名] 例如:int Base::a。
3.当静态成员变量被声明为private类型时,不能用类名::变量名 为静态成员变量赋值。
当声明为public 则可以。
4.当用类名调用静态成员函数时,调用符号是::,而不是java里的.号,也不是指针的->号