[C++]静态持续变量static

复习自《C++ Primer Plus(第六版)中文版》

1.静态持续变量(p309)

  • 和C语言一样,C++也为静态存储持续性变量提供了三种链接性:外部链接性(可在其他文件中访问)内部链接性(只能在当前文件中访问)无链接性(只能在当前函数或代码块中访问)
  • 这三种链接性都在整个程序执行期间存在,与自动变量相比,它们的寿命更长。
  • 由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理它们。
  • 编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存在。
  • 另外如果没有显式地初始化静态变量,编译器将把它设置为0。在默认情况下,静态数组和结构将每个元素或成员的所有位都设置为0。
int global=1000;//外部链接性
static int one_file=50;//内部链接性
int main()
{
    ...
}
void funct1(int n)
{
    static int count=0;//无链接性
    int llama=0;
    ...
}
void funct2(int q)
{
    ...
}

正如前面指出,所有静态持续变量(上述的global、one_file和count)在整个程序执行期间都存在。在funct1()中声明的变量count的作用域为局部,没有链接性,这意味着只能在funct1()函数中使用它,就像自动变量llama一样。
然而,与llama不同的是,即使在funct1()函数没有被执行时,count也留在内存中。
global和one_file的作用域都为整个文件,即在从声明位置到文件结尾的范围内都可以被使用。具体地说,可以在main()、funct1()和funct2()中使用它们。
由于one_file的链接性为内部,因此只能在包含上述代码的文件中使用它;
由于global的链接性为外部,因此可以在程序的其他文件中使用它(相当于在其前面声明extern)。

1.1.静态持续性、内部链接性

将static限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。在多文件程序中,内部链接性和外部链接性之间的差别很有意义。链接性为内部的变量只能在其所属的文件中使用;但常规外部变量都具有外部链接性,既可以在其他文件中使用。

那如果要在其他文件中使用相同的名称来表示其他变量,该如何办呢?

//file1
int errors=20; //外部声明
...
//file2
int errors=5; //无法识别是file1还是file2的定义
void froobish(){
    cout<<errors;//错误
}

上述做法违反了单定义规则。file2中的定义试图创建一个外部变量,因此程序将包含errors的两个定义,这是错误的。
但如果文件定义了一个静态外部变量,其名称与另一个文件中声明的常规外部变量相同,则在该文件中,静态变量将==隐藏 ==常规外部变量:

//file1
int errors=20;//外部声明
...
//file2
static int errors=5;//只能在file2中定义
void froobish(){
    cout<<erros;//使用file2定义的errors
}

这样没有违反单定义规则,因为关键字static指出标识符errrors的链接性为内部,因此并非要提供外部定义。

2.静态成员

class StringBad
{
private:
    char *str;
    int len;
    static int num_strings;
public:
    StringBad(const char *s);
    StringBad();
    ~StringBad();
    static int HowMany(){return num_strings;}
}

对于其中的静态成员num_strings,静态类成员有一个特点:无论创建了多少对象,程序都只创建一个静态类变量副本。也就是说,类的所有对象共享同一个静态成员,就像家中的电话可供全体家庭成员共享一样。
其次,我们不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但不分配内存。对于静态类成员,可以在类声明之外使用单独的语句来进行初始化,这是因为静态类成员是单独存储的,而不是对象的组成部分。

注意: 静态数据成员在类声明中声明,在包含类方法的文件中初始化。初始化时使用作用域运算符来指出静态成员所属的类。但如果静态成员是const整数类型或枚举型,则可以在类声明中初始化。)

2.1静态成员函数

可以将成员函数声明为静态的(函数声明必须包含关键字static,但如果函数定义是独立的,则其中不能包含关键字static),这样做有两个重要后果。

首先,不能通过对象调用静态成员函数;实际上,静态成员函数甚至不能使用this指针。如果静态成员函数是在公有部分声明的,则可以使用类名和作用域解析运算符来调用它。例如上面实例中调用时可以如下:

int count=StringBad::HowMany();

其次,由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员。例如,静态方法HowMany()可以访问静态成员num_string,但不能访问str和len。

总结

static的作用主要是:

  • 保持变量内容的持久 :存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。
  • 默认初始化为0(static变量) :其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘\0’;太麻烦。如果把字符串定义成静态的,就省去了这个麻烦
  • 隐藏:当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。(如上述file1和file2的例子)
  • C++中的类成员声明static :如下

(1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致 了它仅能访问类的静态数据和静态成员函数。

(2)不能将静态成员函数定义为虚函数。

(3)由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊 ,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember函数指针”。

(4)由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就 产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X W indow系统结合,同时也成功的应用于线程函数身上。 (这条没遇见过)

(5)static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问 时间,节省了子类的内存空间。

(6)静态数据成员在<定义或说明>时前面加关键字static。

(7)静态数据成员是静态存储的,所以必须对它进行初始化。 (程序员手动初始化,否则编译时一般不会报错,但是在Link时会报错误)

(8)静态成员初始化与一般数据成员初始化不同:

初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;
初始化时不加该成员的访问权限控制符private,public等;
初始化时使用作用域运算符来标明它所属类;
所以我们得出静态数据成员初始化的格式:
<数据类型><类名>::<静态数据成员名>=<值>

(9)为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们有重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。

总结转自:https://www.cnblogs.com/songdanzju/p/7422380.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值