# c++静态成员详解

静态成员由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函数不声明为静态, 就会出错, 因为参数不匹配。

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容