0%

C++ 中static和const的使用

static

  • 作用域隐藏。当一个工程有多个文件的时候,用static修饰的函数或变量只能够在本文件中可见,文件外不可见。
  • 全局生命周期。用static修饰的变量或函数生命周期是全局的。被static修饰的变量存储在静态数据区。
  • static修饰的变量默认初始化为0。(初始化会存在data段,未初始化是在bss段)
  • 在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的静态成员变量。

1、修饰普通变量:修改变量的存储区域和生命周期,使变量存储在静态区,在main函数运行前就分配了空间,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它。

2、修饰普通函数,表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为了防止与他人命令函数重名,可以将函数定位为static。

3、修饰成员变量,属于整个类所拥有,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见。静态成员变量是静态存储的,所以必须对它进行初始化

4、修饰成员函数,属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

static成员函数不能为virtual:

1.static成员不属于任何类对象或类实例,所以即使给此函数加上virutal也是没有任何意义的。

2.静态与非静态成员函数之间有一个主要的区别。那就是静态成员函数没有this指针。

虚函数依靠vptr和vtable来处理。vptr是一个指针,在类的构造函数中创建生成,并且只能用this指针来访问它,因为它是类的一个成员,并且vptr指向保存虚函数地址的vtable.

对于静态成员函数,它没有this指针,所以无法访问vptr. 这就是为何static函数不能为virtual.

虚函数的调用关系:this -> vptr -> vtable ->virtual function

通过下面例子可以确定,当类增加了一个虚函数后,类的大小会增大4字节(指针的大小).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Test
{
public:
      int _m;
};
sizeof(Test) = 4;

// 加入虚函数后
class Test
{
public:
      int _m;
      virtual void fun();
};
sizeof(Test) = 8

static成员函数不能为const函数

C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的中参数的值,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时const的用法和static是冲突的。

volatile的道理也是如此。

const

1、修饰变量,说明该变量不可以被改变;

2、修饰指针,有以下三种情况:

  • A: const 修饰指针指向的内容,则内容为不可变量。
  • B: const 修饰指针,则指针为不可变量。
  • C: const 修饰指针和指针指向的内容,则指针和指针指向的内容都为不可变量。

对于 A:

1
const int *p = 8;

则指针指向的内容 8 不可改变。简称左定值,因为 const 位于 * 号的左边。

对于B:

1
2
3
4
5
int a = 8;
int* const p = &a;
*p = 9; // 正确
int b = 7;
p = &b; // 错误

对于 const 指针 p 其指向的内存地址不能够被改变,但其内容可以改变。简称,右定向。因为 const 位于 * 号的右边。

对于C:则是 A 和 B的合并

1
2
int a = 8;
const int * const p = &a;

这时,const p 的指向的内容和指向的内存地址都已固定,不可改变。

对于 A,B,C 三种情况,根据 const 位于 号的位置不同,总结三句话便于记忆的话:*”左定值,右定向,const修饰不变量”

3、常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改;

4、修饰成员函数,说明该成员函数内不能修改成员变量,如果要修改可以使用mutable关键字修饰这个成员变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>
using namespace std;
class Test
{
public:
Test(int _m,int _t):_cm(_m),_ct(_t){}
void Kf()const
{
++_cm; // 错误
++_ct; // 正确
}
private:
int _cm;
mutable int _ct;
};

int main(void)
{
Test t(8,7);
return 0;
}

const和define的区别

类型和安全检查不同

宏定义是字符替换,没有数据类型的区别,同时这种替换没有类型安全检查,可能产生边际效应等错误;

const常量是常量的声明,有类型区别,需要在编译阶段进行类型检查

编译器处理阶段不同

宏定义是一个”编译时”概念,在预处理阶段展开;

const常量是一个”运行时”概念,在程序运行使用,类似于一个只读行数据

存储方式不同

宏定义是直接替换,不会分配内存,存储于程序的代码段中;我理解的是给出的是立即数,有多少次使用就进行多少次替换,在内存中会有多个拷贝,消耗内存大;

const常量需要进行内存分配,存储于程序的数据段中,编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放;const局部变量存储在栈中,代码块结束时释放

定义域不同

1
2
3
4
5
6
7
8
9
10
void f1 ()
{
#define N 12
const int n 12;
}
void f2 ()
{
cout<<N <<endl; //正确,N已经定义过,不受定义域限制
cout<<n <<endl; //错误,n定义域只在f1函数中
}

定义后能否取消
宏定义可以通过#undef来使之前的宏定义失效

const常量定义后将在定义域内永久有效

1
2
3
4
5
6
7
8
void f1()
{
#define N 12
const int n = 12;

#undef N //取消宏定义后,即使在f1函数中,N也无效了
#define N 21//取消后可以重新定义
}

是否可以做函数参数

宏定义不能作为参数传递给函数

const常量可以在函数的参数列表中出现

static和const联合使用

static与const作用:声明一个只读的静态变量

static const,既是只读的,又是只在当前模块中可见的

参考:

https://www.runoob.com/w3cnote/cpp-const-keyword.html

喜欢你就打赏一下