静态成员由static关键字修饰, 分为静态成员变量和静态成员函数, 特点是归属于类所有, 作用域是整个类, 而不是某一个对象, 换句话说静态成员存储于全局数据区的, 不会因实例对象不同而有所改变。 下面对其几个注意事项进行描述:
一, 静态成员在类内声明, 类外定义, 声明时需要加上static关键字, 定义时不加static关键字, 但要加上作用域即类(类名::).
class A{
private:
static int b; //静态成员变量
static void by_use(string s); //静态成员函数
public:
A(){}
};
int A::b = 20; //类外定义静态成员变量
void A::by_use(string s){ //定义静态成员函数
std::cout << "by_use:" << s << b<< std::endl;
}
int main(){
...
return 0;
}
需要说明的几个要点:
const static类型的静态成员变量可以在类内进行定义。 个人觉得原因如下:静态变量是被所有类对象共享的, 当然每一个类对象都可以进行访问, 如果静态成员变量在类内定义的话, 每一个对象初始化时都有可能对该静态变量重新赋值, 从而造成其他对象的访问错误。如果定义为const static类型的话, 那么该变量的值不可变, 那么就不存在变量值变化的风险, 所以可以类内定义。
静态成员函数也可以在类内定义, 我的理解是这样的, 函数不存在多次初始化赋值的现象, 因而也就不存在因某个对象初始化, 而不小心改变了值, 进而影响其他对象的使用的情况的发生。
-
我们上面直接说了在类内声明静态变量的时候需要使用关键字static进行修饰,在类外定义static时 不要加上关键字, 只需要加上作用域就好。下面阐述原因:1, 首先介绍一下static的作用, 可以总结如下: static修饰的变量(函数)会存储在全局数据区域, 使其生命周期为整个程序的运行期间, 作用域为定义该静态变量的函数、文件或者类, (这里需要理解生命周期和作用域是不一样的), 举个简单例子:
void func(int b){ static int a = 10; a = b; ... } int main(){ func(20); func(30); ... return 0; }上面的例子中我们在func()函数中定义了一个静态变量a, 该变量在执行main()函数之前就已经被初始化, 且只被初始化一次, 它的生命周期是整个程序的运行期间, 但是作用域仅限于定义它的func()函数中, func()函数运行完之后, 会回收资源, 所有的普通成员变量会从栈中弹出, 但是静态变量存储于全局数据区, 所以它没有销毁, 程序运行期间会一直存在(这是他的生存周期), 但是随着func()函数运行结束, 它也会变得不可见, 或者更准确来说不能被其他函数“看见”(这由它的作用域决定), 下次我们再一次调用func()时, 静态变量a会被再次使用。就像上面的例子, 程序的最后, a的值应该是30。
2,如果你看明白了上面的例子, 你应该明白被static修饰的变量, 有这样的特性:生存周期为整个程序的运行时间, 作用域为定义该变量的块内(函数或者类)。 好了, 我们回到正题, 在类内使用static声明静态变量, 是使变量可以一直存在, 而且作用于该类。在类外定义该变量的时候不准使用static, 是可以保证该变量的作用仅为定义它的类, 而不是当前文件。想象一下, 如果在类外使用static定义了某静态变量, 是不是就将该变量的作用域设置为当前文件了(其他文件不可见), 那就不是类成员奥。
二、this指针与静态成员
1, this指针的作用
我们在类的成员函数中可以去访问该类的其他成员时, 其中非静态函数可以访问静态和非静态成员, 静态函数只能访问静态成员, 非静态函数通过this指针去访问非静态成员,通过作用域访问静态成员。 当然你没有使用this指针去访问非静态成员也不会出错, 因为在编译阶段编译器帮你加上了this指针。举个例子:
class A{
private:
//定义成员变量
int a;
static int b;
//定义成员函数
void to_use(){ //用于讲解的例子
a = 20;
b = 150;
use("hello word");
}
void use(string s){
std::cout << s << std::endl ;
}
public:
//构造函数
A(){
a = 0;
}
A(int a){
this->a = a;
std::cout << a << std::endl;
}
};
int A::b = 50;
注意看to_use(string s)这个函数, 这里面调用了类A的成员变量a、b,和成员函数use,其中变量b是静态的。好了, 按照我们上面所述, 编译器在编译时会自动在代码中加上this->去访问非静态成员, 而对于静态成员不需要做什么特别的操作, 因为静态成员的作用域为该类, 只需要直接调用就行。所以to_use(string s)函数中的代码会被编译器改为:
void to_use(){ //用于讲解的例子
this->a = 20;
A::b = 150;
this->use("hello word");
}
2, this指针作为参数
由(1)中所知, 非静态成员函数要想访问其他非静态成员必须使用this指针, 所以非静态成员函数在被调用时隐含传入一个this指针。与之对应的,静态成员函数在被调用时不会被传入this指针, 其参数就是函数声明的那几个。
那么问题来了, 如果我们在某非静态成员函数中直接调用另一个成员函数时, 会将this指针作为隐含的参数传递过去, 这没有问题(就像(1)中to_use()调用use(string s)那样, 会将this传过去), 但是如果被调用的函数是作为回调函数存在, this指针就无法作为参数传递给这个回调函数了, 所以回调函数必须定义为静态成员函数。具体原因以及回调函数概念请见下节
3, 回调函数
将某函数A作为参数作为参数传递给另一给另一个函数B, 这个函数A就是回调函数。如下
qsort(arr0, len0, sizeof(int), &int_cmp);
qsort(arr0, len0, sizeof(int), int_cmp);
int_cmp是定义好的一个函数, 在上面的两个例子中, int_cmp就是回调函数, 需要注意的是c++中函数作为参数传递时, 传入参数名或者对函数进行取地址操作在传参效果是一样的, 因为该函数在被调用的时候也是对函数进行取地址操作。
回调函数一定声明为静态成员
在类成员函数中, c++是默认使用this对其他成员进行访问的, 当在某一成员函数中调用其他普通成员函数时, this也会作为默认参数进行传递(编译器会做事情), 但是在该函数体内出现的回调函数则没有这个待遇, 也就是说在调用回调函数的时候, 不会将this作为参数传递给回调函数, 那么如果回调函数被声明为普通成员函数的话, (即没有声明为静态类成员函数), 那么参数的个数是不匹配的, 因为作为普通成员函数在调用时期待被传入一个this指针的参数, 但回调函数被调用时却没有接收到该参数, 所以一定会出错, 故回调函数一定声明为静态成员函数。例子:
class A{
private:
//定义成员变量
int a;
static int b;
//定义成员函数
void to_use(){ //用于讲解的例子
a = 20;
b = 150;
use("hello word");
}
void use(void (*ptr)(string)){ //参数为回调函数
ptr("nihao");
}
static void by_use(string s){ //静态成员函数
std::cout << "by_use:" << s << b<< std::endl;
}
public:
//构造函数
A(){
a = 0;
}
A(int a){
this->a = a;
std::cout << a << std::endl;
}
};
int A::b = 50;
上例中, use(void (ptr)(string))函数的参数为函数, 是典型的的回调函数, 在to_use()中调用了use(void (ptr)(string)), this指针会作为参数传递给use函数, 所以use函数可以为非静态, 但是by_use函数作为回调函数在use()函数中使用, this指针是不会进行传递的, 所以这里如果by_use函数不声明为静态, 就会出错, 因为参数不匹配。