【深度C++】之“static”

0. 关于static

static是C++语言中的一个关键字。它的用法很多,且容易混淆,经常在各大考试、面试中出现,本文对其进行总结归纳。

static共有如下几种用法:(可以直接看第5节总结)

  1. 修饰局部变量
  2. 修饰全局变量
  3. 修饰普通函数
  4. 修饰类的成员(变量、函数)

1. static修饰局部变量

1.1 局部变量

首先明白局部变量的定义:

形参和函数体内部定义的变量统称为局部变量(local variable)

局部变量仅在函数的作用域可见,会隐藏外部作用域的同名变量。这里的局部指的是作用域的局部。

void func() {
    // 这是一个局部变量,同时也是自动对象
    int i = 0;
    return;
}

在C++中,变量对象的概念通常是等价的。

加入了static之后,为了便于区分,引入自动对象的概念。

将只存在于块执行期间的对象称为自动对象

如之前定义的函数func()中的变量i就是一个自动对象。

函数的控制路径经过变量时创建对象,到达块末尾时销毁它。

举上例来说,func()执行到语句int i时候创建对象ifunc()执行完毕后销毁i

自动对象若没有显示初始化,则执行默认初始化(参考【深度C++】之“初始化”)。

1.2 局部静态对象

那有没有办法,不让func()执行完毕后,销毁i

可以将i定义为局部静态对象,改变他的生存期

void func() {
    // 这是一个局部变量,同时也是局部静态对象
    static int i = 0;
    return;
}

函数的控制流第一次经过static int i = 0时创建对象i,执行完毕后不销毁i

局部静态对象若没有显示初始化,则执行值初始化(参考【深度C++】之“初始化”)。

这样就有意思了,考虑如下代码:

int count_calls() {
    static int ctr = 0;
    return ++ctr;
}

int main() {
    for (int i = 0; i < 10; ++i)
        cout << count_calls() << endl;  // 输出1到10(包括10)
    return 0;
}

函数的控制流第一次经过static int ctr = 0时创建对象ctr并初始化为0;每次调用count_calls()时,ctr存在,且等于上一次退出函数时的值,因此函数每次给ctr加1。

1.3 小结

局部对象

2. staic修饰全局变量

2.1 全局变量

局部就有全局

在所有函数体之外定义的对象称为全局变量

全局变量存在于程序的整个执行过程中,在程序启动时被创建,程序结束时被销毁。

全局变量的作用域在整个程序范围内都可以使用,在其他文件中可以添加extern关键字来声明,使得别的文件也可以使用(见下面的例子)。

2.2 全局静态变量

static修饰全局变量后,改变了它的作用域,使得变量只能在本文件中被访问到。

// file1.cpp
extern void print_sz();

int sz = 16;

int main() {
    print_sz();  // 程序正常工作,输出16
    return 0;
}
// file2.cpp
#include <iostream>

extern int sz;

void print_sz() {
    std::cout << sz << std::endl;
}

上例中,在file1.cpp中使用了file2.cpp中的函数print_sz(),打印了定义在file1.cpp中的sz

若我们将sz声明为static

// file1.cpp
extern void print_sz();

static int sz = 16;

int main() {
    print_sz();  // 链接错误,找不到sz
    return 0;
}

可以看出,static改变了sz的作用域,使其只能在file1.cpp中被使用。

3. static修饰普通函数

static修饰普通函数的作用和全局变量类似,改变函数的作用域,使其只能在本文件中使用。

// file1.cpp
extern void print_sz();

int sz = 16;

int main() {
    print_sz();  // 链接错误,找不到print_sz
    return 0;
}
// file2.cpp
#include <iostream>

extern int sz;

static void print_sz() {
    std::cout << sz << std::endl;
}

上例将原来普通函数print_sz添加声明为static void print_sz(),在执行程序的时候会出现链接错误。

4. static修饰类的成员

4.1 静态成员

有的时候需要类的一些成员与类本身直接相关,而不是与类的各个对象实例保持关联,如银行账户类的基准利率。当我调整该利率的时候,所有使用该利率的银行账户实例都得到了调整。

此时可以使用静态成员,包括静态成员变量和静态成员函数。

4.2 定义静态成员

如下示例展示了如何定义静态成员变量和静态成员函数(内部与外部):

// Account.h
class Account {
private:
    double amount;
    static double interest_rate;  // 声明一个静态成员变量
    static double init_rate();
public:
    void Calcuate() { amount += amount * interest_rate; }
    // 演示如何在类内定义静态成员函数
    static double GetRate() { return interest_rate; }
    static double SetRate(double);
};
// Account.cpp
#include "Account.h"

// 必须在类的外部定义和初始化每个静态成员变量
// 可以写在.h文件中,推荐写在此处,与非内联成员在一起定义
double Account::interest_rate = init_rate();

// 私有的静态成员函数写法和平常一样
double Account::init_rate() {
    return 0.025;
}

// 演示如何在类外定义静态成员函数
double Account::SetRate(double new_rate) {
    interest_rate = new_rate;
}

4.3 使用静态成员

有两种方式:

  1. 使用作用域运算符::直接访问静态成员:
#include "Account.h"

int main() {
    double r = Account::GetRate();
}
  1. 使用类的对象、引用或指针访问静态成员
#include "Account.h"

int main() {
    Account ac1;
    double r = ac1.GetRate();
}

静态成员函数只能使用静态变量。

5. 总结

  1. 修饰局部变量:改变生存期,函数结束后不销毁;
  2. 修饰全局变量:改变作用域,其他文件不可访问;
  3. 修饰普通函数:改变作用域,其他文件不可调用;
  4. 修饰类的成员:使成员与类关联
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值