const 那些事
强烈推荐看:《C++编程思想-8章常量》。
1. const 含义 const=constant, 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。 举个例子,就是 const int test_a = 12, 你是没法给 test_a 赋值的。
const 关键字指定变量的值是常量,并通知编译器防止程序员对其进行修改。
经典一问:const 变量是否可以被修改? 答1:可以。const 修饰的始终是变量,如果const 变量位于栈区,那么是可以通过取地址修改。
但是,如果const 变量位于全局,那么是不可以修改的。
1 2 3 4 5 6 7 8 9 10 const int kGlobalValue = 312 ;int main () { const int const_value = 100 ; int * ptr = (int *)&const_value; *ptr = 200 ; int * ptr_globale = (int *)&kGlobalValue; *ptr_globale = 400 ; }
答2:Linux上 全局常量是放在 .rdata段的,该段只读,所以尝试修改就会报错。为什么C++无法修改全局静态常量?(Linux平台示例-知乎)
const作用
定义常量
const int a=100;
类型检查
const常量有类型,编译器会进行安全检查;#define宏定义只是字符串替换,不进行安全检查;
#define 也是有类型的,详细见:讨论#define和const的类型问题
const 定义的变量只有类型为整数或枚举,且以常量表达式初始化时才能作为常量表达式。
其他情况下它只是一个 const 限定的变量,不要将与常量混淆。
防止修改,起保护作用,增加程序健壮性
修饰函数参数等,如果参数被修改会保存。
1 2 3 void f (const int i) { i++; }
节省空间,避免不必要的内存分配
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数。
const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。
理解常量和常量表达式 理解常量和常量表达式——掘金blog
const的存储位置探究(Todo)
const修饰的量不是常量,仅仅是个只读量。在编译的时候全部替换赋值的const常量,类似#define替换; 在运行的时候该const变量可通过内存进行修改。
总结来说就是,const修饰的数据,编译的时候会进行替换,这样被赋值的数据运行的时候数据是编译期定好的。 即使你修改了 const 修饰的变量,被赋值的数据不会因为运行时的修改而变化。 但是const 能修改的前提必须是可读可写的,比如栈上的变量,全局的就不行。
CSDN关于const存储位置的讨论 CSDN总结的blog, 我觉得写的还可以
const对象的范围 默认为文件局部变量。 注意: 非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。
非const修饰的变量在不同文件的访问
1 2 3 4 5 6 7 8 9 int ext = 132 ; extern int ext;int main () { int test_ext = ext; }
const 修饰的变量在不同文件的访问 const 修饰的想外部访问,定义的时候就得加 extern;且需要初始化。
1 2 3 4 5 6 7 8 9 10 const int ext_const_def = 132 ; extern const int ext_const_extern = 312 ; extern const int ext_const_extern;int main () { int test_ext = ext_const_extern; }
extern const 未初始化会报链接错误:无法解析的外部符号;
const用法 const修饰普通类型的变量 1 2 3 4 5 6 7 8 const int b = 10 ; b = 0 ; int const b = 10 ;const string s = "helloworld" ;const int i,j=0
作为常量,直接赋值修改是不行的哦! 作为常量,必须初始化哦!
C2734 “const_value”: 如果不是外部的,则必须初始化常量对象。
const与指针 1 2 3 4 const char * a; char const * a; char * const a; const char * const a;
常量指针: 指向的对象是常量。不能修改指针所指向的内容。
const 位于 * 的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
1 2 3 4 5 6 7 8 9 10 int main () { int const_test_a = 123 ; const int * const_ptr_a; const_ptr_a = &const_test_a; int * ptr_test_a = &const_test_a; *ptr_test_a = 312 ; }
常量指针的注意点:
const_ptr_a 指针不用赋初值,但不能通过 const_ptr_a 修改所指向的对象的值;
不能使用void指针保存const对象的地址,必须使用const void 类型的指针保存const对象的地址;
可以把非const对象的地址赋值给 const 对象的指针;但不能通过 const 定义的那个指针修改对象值。
指针常量: 指针本身是常量。不能修改指针的地址。
const 位于 * 的右侧,const就是修饰指针本身,即指针本身是常量。
指针常量的注意点
const指针必须初始化,且const指针的值不能修改。(就是说const指针存储的地址不能动哦)
1 2 3 4 5 6 7 8 int main () { int num=0 ; int * const ptr=# int * t = # *t = 1 ; }
指向常量的常指针 1 2 3 4 5 int main () { const int p = 3 ; const int * const ptr = &p; }
知乎关于const指针讲的比较容易懂的blog 通俗易懂理解什么是常量指针和指针常量——知乎
const应用到函数中
const修饰函数返回值
const int —— 本身无意义,函数返回本身就是要赋值给其他变量的。
const int* —— 指针指向的内容不变
int* const —— 指针本身即指针的地址不能变
const修饰函数参数
传过来的参数以及指针本身在函数内不可变,没有意义1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void func (const int var) ; void func (int *const var) ; void TestConstPtr (int *const var) { int test = *var; } int main () { int num = 3211 ; int * const ptr_num = # TestConstPtr (ptr_num); }
值传递方式, 形参本身会产生临时变量,参数无需保护,所以加不加const没有意义。 const int var 和 int *const var 都是值传递。 理解下 int *const var 为什么没有意义? 答1:const修饰的是指针本身,所以此处只是修饰 var 本身不变; 答2:另外,指针传递的时候,var 指向的数据依然可能被修改,所以只限制 var 本身没有意义。
参数指针指向的内容为常量不可变 1 void StringCopy (char *dst, const char *src) ;
试图修改 src 时,编译会报错。
参数为引用,为了增加效率同时防止修改 1 2 3 4 5 6 7 8 9 10 void funcA (const A a) ; void funcA (const A& a) ; void funcA (const A& a) ; void funcA (const std::string& value) ; void funcA (const int & a) ;
1. 引用只是参数的别名而已,不需要产生临 时对象。 2. const 限定的引用,可以避免引用被修改。
const 引用传递的使用场景
非内部数据做输入参数:建议使用 const 引用,提高效率;
内部数据类型做输入参数:不建议用,比如 int、double 等类型;
const类中的用法
查看C++类的内存结构方法
查看所有类:/d1 reportAllClassLayout
查看单个类:/d1 reportSingleClassLayoutXXX(XXX为类名)
/d1 参数统一放到 C/C++ → 命令行:其他选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Apple {public : Apple (int i) : apple_number (i){ app_id = i; } int FuncA () const { int rest = 123 ; FuncB (); return rest; } int FuncB () { return 123 ; } private : int people[100 ]; const int apple_number; const int app_code = 312 ; int app_id; static int s_test_a; }; int Apple::s_test_a = 212 ;
查看C++类的内存结构方法_例子写的很好的blog
理解this指针 先说编译器怎么做的?
this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给this。
this 形参是隐式的,并不出现在代码中,而是编译阶段由编译器默默地将它添加到参数列表中。且只有非静态成员函数哦!
成员函数最终被编译成与对象无关的普通函数,除了成员变量,会丢失所有信息。 所以编译时要在成员函数中添加一个额外的参数,把当前对象的首地址传入,以此来关联成员函数和成员变量。
示例:int FuncA(int posx, int posy); 编译时可能变成了 int FuncA(Apple* this, int posx, int posy) 【不一定是这个结果,只用于示例说明】
总结一下this是什么? 答: this是编译器加给c++类的非static成员函数的额外参数,用于管理类的成员函数和变量。作为参数也有类型限定,比如使用const。
作为参数,this指针也是有类型的。
this的类型取决于成员函数的声明情况。 const 方法时,指针类型就是 const Apple, 非const 就是 Apple 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int FuncA (int posx) { appid = posx; return 123 ; } int FuncB (int posx) const { appid = posx; FuncA (); return 123 ; } int FuncA (Apple*this , int posx) { this ->appid = posx; return 123 ; } int FuncB (const Apple*this , int posx) const { this ->appid = posx; this ->FuncA (); return 123 ; }
C++中的this指针_geeksforgeeks C语言中文对于this指针的讲解_最后的this是什么说的还可以
const和#define区别
参考链接
主要是参考Github上的c++那些事:https://github.com/Light-City/CPlusPlusThings
MSDN对于const的说明:https://learn.microsoft.com/zh-cn/cpp/cpp/const-cpp?view=msvc-170
极客笔记c++ const的使用:https://deepinout.com/cpp-tutorial/cpp-tutorial-const-usage.html