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

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

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

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

我整理如下(已更新)


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


    [cpp deep dive] 一些奇怪的关键字_不那么奇怪的static_第2张图片
    知乎上的一个答案(链接
  • 附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的验证:
[cpp deep dive] 一些奇怪的关键字_不那么奇怪的static_第3张图片
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<

输出

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

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

  • 类的static成员,类内只是声明,需要在类外定义,否则链接阶段出错:未定义的标识符(ld2: undefined reference to...)。(phase 1)
    • 类的static成员,类外定义的数据类型需要跟声明时是相同的.定义的语法是
      ::[ = 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 (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<

输出

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:

  • http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf (反正我已经暂时看晕了)

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

  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] 一些奇怪的关键字_不那么奇怪的static)