美文网首页
[cpp deep dive] 一些奇怪的关键字_不那么奇怪的s

[cpp deep dive] 一些奇怪的关键字_不那么奇怪的s

作者: Quasars | 来源:发表于2016-07-23 11:56 被阅读145次

    个人理解,如果有误请及时指出,我将非常感谢!
    还是从一个题目开始吧~

    • 关于C++/Java类中的static成员和对象成员的说法正确的是:
      A:static成员变量在对象构造时候生成
      B: static成员函数在对象成员函数中无法调用
      C: 虚成员函数不可能是static成员函数
      D: static成员函数不能访问static成员变量
      参考答案:C

    step 0. static变量的存放位置 - 静态存储区

    我整理如下(已更新)


    静态/动态存储区.png
    • 这里的静态/动态指的是:
      • 静态:在程序运行过程中,1. 空间大小不变,这些空间在程序结束后由系统回收. 2.存在这些区域的变量生命周期贯穿整个程序(进程).
      • 动态:程序运行过程中,1. 大小可变,有可能会动态地进行申请/释放,2. 这里的变量声明周期不是贯穿整个程序,而是可能由程序员控制的。
      • 相对的,需要区分静/动态与读写/只读属性.
    • 附:


      知乎上的一个答案(<a href=https://www.zhihu.com/question/28346079>链接</a>)
    • 附2:验证一下:
      1 #include "common.h"                                                                                                                                                                        
      2 int g_uninitial;                                                                
      3 int g_uni[1024];                                                                
      4                                                                                 
      5 int g_initial_d = 1000;                                                         
      6                                                                                 
      7 int main()                                                                      
      8 {                                                                               
      9     static int local_static_var = 100;                                          
     10     static int local_static_un_var;                                             
     11     int stack_var = 100;                                                        
     12     const int ss = 100;                                                         
     13                                                                                 
     14     printf("constant var          -  %p\n", &ss);                               
     15     printf("local var             -  %p\n", &stack_var);                        
     16                                                                                 
     17     printf("lo_sta_uninit(.bss)   -  %p (value %d)\n", &local_static_un_var, local_static_un_var);
     18     printf("global_uninited(.bss) -  %p (value %d)\n", &g_uninitial, g_uninitial);
     19                                                                                 
     20                                                                                 
     21     printf("global_inited(.data)  -  %p\n", &g_initial_d);                      
     22     printf("local_static(.data)   -  %p\n", &local_static_var);                 
     23     printf("constant              -  %p\n", "abcd");                            
     24     printf("MAIN's addr(.text)    -  %p\n", main);                              
     25                                                                                 
     26     return 0;                                                                   
     27 }
    

    结果输出.

    work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
    constant var          -  0x7ffc9760ee6c  <--------所以const变量本身还是位于堆栈内部,const仅仅是个只读标记而已.
    local var             -  0x7ffc9760ee68
    lo_sta_uninit(.bss)   -  0x6020a4 (value 0) 
    global_uninited(.bss) -  0x601080 (value 0)  <------至于谁高谁低就需要了解装入/分配机制了
    global_inited(.data)  -  0x601058
    local_static(.data)   -  0x60105c
    constant              -  0x400982 <-------------字符串常量,位于main程序所在位置上面一点
    MAIN's addr(.text)    -  0x40070d
    
    • 总结一下要点:
    • 字符串常量位于.text段.
    • 全局初始化变量(包括static)位于.data段.
    • 全局未初始化变量位于.bss段,且以0填充(linux).
    • 关于 malloc和calloc的验证:
    code(代码贴图差评!@!!)
    work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
    0 0 0 0 0 0 0 0 16 66 80 11 -128 52 7 -16 32 68 -112 0 -126 84 11 113 48 64 -48 27 -125 116 15 -16 64 72 17 35 -124 -108 0 114 80 74 81 43 -128 -76 23 -14 96 76 -112 0 -122 -44 27 115 112 64 -47 59 -121 -12 31 -16 -128 80 18 67 -120 20 0 116 -112 82 82 75 -128 53 39 -12 -96 84 -112 0 -118 85 43 117 -80 64 -46 91 -117 117 47 -16 -64 88 19 99 
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    

    step 1. C中的static

    • c中的static关键字有个重要作用,是使该变量(全局static变量)仅本文件可见.
    • c中局部static变量,每次访问时都是对同一个变量做操作,并不会因为函数退出而销毁.
    #include "common.h"
    
    void functest(){
        static int x = 0;
        x++;
        cout<<x<<endl;
    }
    
    int main()
    {
        functest();
        functest();
        return 0;
    }
    

    输出

    work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
    1
    2
    

    step 2. 面向对象中的static. 如何使用.

    • 类的static成员,类内只是声明,需要在类外定义,否则链接阶段出错:未定义的标识符(ld2: undefined reference to...)。(phase 1)
      • 类的static成员,类外定义的数据类型需要跟声明时是相同的.定义的语法是
        <Type> <class-name>::<member_name>[ = initial_value];
    • 类的static成员不占该类大小的空间_因为static成员是位于.data段的。(phase 1)
    • 附3:(验证ok)
      1 #include "common.h"                                                                                                                                                                        
      2 int g_uninitial;                                                                
      3 int g_uni[1024];                                                                
      4                                                                                 
      5 int g_initial_d = 1000;                                                         
      6 class base{                                                                     
      7     public:                                                                     
      8         static int ss1;                                                         
      9         static double ss2;                                                      
     10         static void func();                                                     
     11 };                                                                              
     12 int base::ss1;                                                                  
     13 double base::ss2 = 100;                                                         
     14                                                                                 
     15 int main()                                                                      
     16 {                                                                               
     17     static int local_static_var = 100;                                          
     18     static int local_static_un_var;                                             
     19     int stack_var = 100;                                                        
     20     const int ss = 100;                                                         
     21                                                                                 
     22     printf("constant var          -  %p\n", &ss);                               
     23     printf("local var             -  %p\n", &stack_var);                        
     24                                                                                 
     25     printf("global_uninited(.bss) -  %p (value %d)\n", &g_uninitial, g_uninitial);
     26     printf("lo_sta_uninit(.bss)   -  %p (value %d)\n", &local_static_un_var, local_static_un_var);
     27     printf("base::ss1             -  %p (value %d)\n", &base::ss1, base::ss1);  
     28                                                                                 
     29     printf("global_inited(.data)  -  %p\n", &g_initial_d);                      
     30     printf("local_static(.data)   -  %p\n", &local_static_var);                 
     31     printf("base::ss2             -  %p\n", &base::ss2);                        
     32                                                                                 
     33     printf("constant              -  %p\n", "abcd");
     34     printf("MAIN's addr(.text)    -  %p\n", main);                              
     35                                                                                 
     36     printf("sizeof(base)  - %ld\n", sizeof(base));                              
     37     return 0;                                                                   
     38 }   
    

    输出

    work@vm1:~/share/toys/CSE274.me/02_Cpp_Intro$ ./test
    constant var          -  0x7fff562b87dc
    local var             -  0x7fff562b87d8
    global_uninited(.bss) -  0x6010a0 (value 0)
    lo_sta_uninit(.bss)   -  0x6020c8 (value 0)
    base::ss1             -  0x6020c0 (value 0)
    global_inited(.data)  -  0x601058
    local_static(.data)   -  0x601068
    base::ss2             -  0x601060
    constant              -  0x400a17
    MAIN's addr(.text)    -  0x40070d
    sizeof(base)  - 1
    

    • 类的非static成员函数,需要通过具体对象调用,不能以类似类名::函数名来调用。 (1)

      • 否则,错误信息将类似error: cannot call member function ‘int base::out()’ without object.
    • 类的非static成员函数可以访问static成员和static成员函数(ok)(2)

    • 类的static成员函数,static成员函数不能访问非static成员。也就是说static成员函数只能访问static成员。(3)

      • 否则,错误信息将类似invalid use of member ‘base::kk’ in static member function
    • 类的static成员函数在声明时语法: static <ret-type> <func-name>(list_of_parameters...);同时,在定义该成员函数时,不需要再写一遍static了。

    • 类的static成员函数不能是virtual的,因此就不具备多态。(后半句存在争议,主要是对“多态”这个词的定义)(4)

      • 类的static成员函数可以是具有不同参数列表的重载函数(overload),但不能是virtual。这里需要区分重载并不是多态.
      • 重载是静态多态(编译期绑定,静态绑定),区别于我们常说的多态(需要在继承关系中发生的即virtual,动态绑定,也就是override,运行时决定)。

    附4:

    (扩展上面的代码)
    #include "common.h"
    int g_uninitial;
    int g_uni[1024];
    
    int g_initial_d = 1000;
    class base{
        public:
            static int ss1;
            static double ss2;
            //virtual static void func2(double k); //error: member ‘func’ cannot be declared both virtual and static.........(4)
            static void func(double k); 
            static void func(int k);
            int out(){//成员函数也不占类的大小.........(2)
                ____("out----");
                ss1++;
                cout<<ss1<<endl;
                func(ss1);          
            }
        private:
            int kk;
    };
    int base::ss1;
    double base::ss2 = 100.2;
    void base::func(double k){
        //cout<<kk<<endl; error: invalid use of member ‘base::kk’ in static member function (3)
        ____("double func---");
        cout<<k<<endl;
        cout<<ss2<<endl;
    }
    void base::func(int kk){
        ____("int func---");
        cout<<kk<<endl;//局部同名变量(参数)
        cout<<ss1<<endl;
    }
    
    
    
    int main()
    {
        static int local_static_var = 100;
        static int local_static_un_var;
        int stack_var = 100;
        const int ss = 100;
        
        printf("constant var          -  %p\n", &ss);
        printf("local var             -  %p\n", &stack_var);
        
        printf("global_uninited(.bss) -  %p (value %d)\n", &g_uninitial, g_uninitial);
        printf("lo_sta_uninit(.bss)   -  %p (value %d)\n", &local_static_un_var, local_static_un_var);
        printf("base::ss1             -  %p (value %d)\n", &base::ss1, base::ss1);
        
        printf("global_inited(.data)  -  %p\n", &g_initial_d);
        printf("local_static(.data)   -  %p\n", &local_static_var);
        printf("base::ss2             -  %p\n", &base::ss2);
        
        printf("constant              -  %p\n", "abcd");
        printf("MAIN's addr(.text)    -  %p\n", main);
    
        printf("sizeof(base)  - %ld\n", sizeof(base));
        ____("----------");
        
        
        base::func(1);
        base::func((double)2);  //overload not override
        //base::out();  error: cannot call member function ‘int base::out()’ without object base::out();............(1)
        
        ____("-----------");
        base bb1;
        bb1.out();
        
        base bb2;
        bb2.out();
        
        return 0;
    }
    

    输出

    constant var          -  0x7ffdb0b98e4c
    local var             -  0x7ffdb0b98e48
    global_uninited(.bss) -  0x6021c0 (value 0)
    lo_sta_uninit(.bss)   -  0x6031e8 (value 0)
    base::ss1             -  0x6031e0 (value 0)
    global_inited(.data)  -  0x602080
    local_static(.data)   -  0x602090
    base::ss2             -  0x602088
    constant              -  0x400d77
    MAIN's addr(.text)    -  0x4009b4
    sizeof(base)  - 4
    ----------
    int func---
    1
    0
    double func---
    2
    100.2
    -----------
    out----
    1
    int func---
    1
    1
    out----
    2
    int func---
    2
    2
    

    step 3. 如何应用到实际工程中

    • c++面向对象可以用static来实现单例模式.很多情况都需要单例,比如某个工程需要一个日志模块。
      你肯定不能这里一个日志那里一个日志,而是整个工程就一个日志,各个模块把日志信息都输入到同一个日志对象中。

    Ref:

    • <a href = http://blog.csdn.net/crayondeng/article/details/24853471>单例模式的探究</a>

    归纳一下(黑字是我的补充):
    上面的链接中给的一些示例:

    1. 单例模式 != 全静态函数(失去了多态性)
    2. 单例模式的饿汉模式(运行时立刻执行初始化,m_*是个本类类型,本类静态成员)
    • 初始化顺序没有保证
    1. 单例模式的懒汉模式(改进版)(当引用对象时才进行初始化,m_*是个本类类型指针,getInstance()函数的局部静态变量)
    • 线程不安全 可以通过加锁(双检锁)来解决
    • 有内存泄漏的危险 可以增加一个destruct函数来解决
    1. 单例模式的代理模式(通过一个静态嵌套类成员来实现)

    step 3. 按上面的建议实现一个单例模式 并在多线程环境中测试

    由于工程经验尚不是很多,4我就暂时不考虑,先来个懒汉模式 + 双检锁(DCL)吧.
    值得一提的是第二个Ref材料中说明了一个问题,就是static静态成员指针可能被编译器优化(主要是执行顺序的问题),导致一些情况下运行不正常,需要增加volatile关键字.(我先不管那么多)

    关于执行顺序主要有2种办法:(目前所知)

    1.内存栅栏

    • levelDB中有2种实现
      • e
      • d

    2.volatile关键字(需要把volatile搞得遍地都是..)

    遗留的问题有:

    • 嵌套代理类
    • volatile关键字

    相关文章

      网友评论

          本文标题:[cpp deep dive] 一些奇怪的关键字_不那么奇怪的s

          本文链接:https://www.haomeiwen.com/subject/yreyjttx.html