const和引用修饰变量和函数的总结

一、对于修饰变量的用法

对于const和&的基础用法就不说了。下面说点之前有误区和容易错的地方

const修饰变量的误区

关于const类型,这里有一个我之前的误区,我以为const定义的时候只能用常量初始化,但是实际上是没有这个规定的。const定义的变量可以用各种const,非const对其进行初始化,甚至可以是函数的返回值之类的

const int a = 5//在编译的时候进行初始化,即类似于#define,进行全文替换
const int b = size();      //在执行的时候初始化
int n =7;
const int c = n;         //在执行的时候初始化
  • 1
  • 2
  • 3
  • 4

这个其实非常显然啦,因为在函数传参的时候,就能看出来,形参可以是const的,但是传进去的实参可以是非const的。[1]

对于这个问题就可以看出中文各种博客上面的说法有多垃圾了,就我看过的80%的博客都说const必须要用常量初始化,同时在编译时就完成了初始化。以后技术问题少看中文博客,之前被误导了那么久。


常量指针和指向常量的指针

指向常量的指针

const int *p,表示指向常量的指针,按照之前上篇文章中的方法C++ 
中复合类型声明的理解
从右到左看,p是一个指针,指向const 
int,表示的意义就是不能通过p来修改p指向的值,而p本身所指向的地址是可以修改的。(此处也可以写成int const *p是一样的)

常量指针

int *const p,表示p本身是一个const,所以p所指的地址不会变,但是地址里的值是可以修改的。

有个需要注意的地方是:常量引用

const int i1 = 1024;
const int &ci1 = i1;
int i2 = 2048;
const int &ci2 = i2;    //允许将const int&绑定到一个普通int上,其实常量引用可以用任何值进行初始化
  • 1
  • 2
  • 3
  • 4

其实常量引用这个说法是一个非常不严谨的说法,因为C++中本身就不允许随便改变引用所绑定的对象。所以所有引用都是常量。而常量引用类比于上面指针的说法,应该是对常量的引用。 
还要记住一点就是常量引用可以用任何值进行初始化,即可以用右值对其初始化

 int i = 1024const int &ri = i*2
  • 1
  • 2

对于这一点,可以用复制构造函数去理解,在C++11标准的右值引用出来之前,就是用常量引用来延长一个右值的生命周期的。其在计算机内的实现可以认为是这样的:

const int tmp = i*2const int &ri = tmp;
  • 1
  • 2

即ri绑定的是一个临时变量。在C最早版本的时候,引用时可以绑定左值和右值的,只不过如果出现上面这种情况,ri绑定一个临时量,如果ri不是const的话,其修改也会导致临时量的修改,而此时临时值已经被销毁了。所以ri必须是const的。


顶层const与底层const

这里有一个顶层const和底层const的区别,正常情况下,const类型的变量可以给任何变量赋值和初始化的。但是指针就不一样了,指针本身是个对象,而其可以指向另外一个对象,所以指针本身是否是const和其所指的是不是一个常量就是两个独立的问题。 
顶层const是指const修饰变量本身,即变量/对象本身是;而底层const则表示这个变量所指向/引用的值不可变,即不能通过这个变量修改其值,只有对常量的引用和指向常量的指针是底层const

int i=0;
int *const r3 = &i;
const int r1 = i;
  • 1
  • 2
  • 3

以上例子是顶层const即对象本身是const的。而

const int *r4 = &i;
const int &r5 = i; 
  • 1
  • 2

则是底层const,其所指的对象是常量,即不能通过自己修改原来数的值 
对于底层const而言,底层const只能赋值给同样是底层的const,即int* r6 = r4会报错。 
注意下面一个问题

const int m = 0;      //m本身是const类型的值
const int* m2 = &m;     //所以&m是一个const int*类型的,即常量取地址得到的是一个底层const
int* const m3 = &m; //所以第二行没有错,第三行会报错:

const int i = 0;   //i是底层const
int &i2 = i;     //报错,因为i2不是底层const 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

第一个报的错误就是 “const int *” 类型的值不能用于初始化 “int *const” 类型的实体 

第二个报的错误就是将 “int &” 类型的引用绑定到 “const int” 类型的初始值设定项时,限定符被丢弃. 

// 可以将const看成是一种责任,如果一个指针是const int*,则它有这个责任保证任何情况下不能通过它修改数据(直接或者间接的),上面的做法就是一种间接的做法,所以不合法。只有对方也同样有这个责任感的时候才可以赋值,即底层赋值给底层const. 同理如果const int** cp = int** cp也是非法的,为什么因为可以间接的修改最终的int数据--> *cp的值是一个没有const责任的指针,所以这种做法也是非法的。


二、对于修饰函数的用法

引用限定符

先看一个神奇的语句

string s1 = "hello ";
string s2 = "world!";
s1+s2 = "wow!";


string("adfasdf") = "123";   //注意此时一定要加string,因为C++对于常量字符串是用const char*数组保存的// char* = "123";// *char = '9'; // Error: Because they point to the same area which is readonly.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

以上这三句话是可以顺利运行的,原因在于s1+s2得到的是一个右值,而string类型的对象,会重载=运算符
string &operator=(const string&) 
而=运算符可以作为一个函数进行调用,而对对象的函数进行调用时是不在乎其是左值还是右值,如(s1+s2).size()肯定是可以运行的,所以上面的那个=号也是可以运行的。 
还有一点需要注意就是=号返回的是一个左值,所以如果对于连等的情况a1 = a2 = a3,其从右向左一次调用=号,等价于a1 = (a2 = a3),而此时调用的是=是复制运算符,而不是移动复制运算法符,因为返回的是左值。 
所以本质上来说,因为函数只能限制传入的参数类型,和返回的类型,并不能限制this本身的类型导致的。

但是现在新标准为了填旧标准的这个坑,就加入了引用限定符(reference qualifier)虽然string这个类为了像前兼容,上述写法还是可以用的,但是对于我们自己写的对象来说可以避免这种情况了。跟const类似,引用限定符是加在参数列表之后的:

Foo &Foo::operator=(const Foo&) &;         //this此时只能是可修改的左值
Foo &Foo::operator=(const Foo&) &&;        //this此时只能是右值
  • 1
  • 2

在函数声明结尾加入&和&&来限制this本身。所以此时再重载=运算符,其后加上&,就能避免之前的那种情况了。 
还有几点需要注意:

  • const和&都只能用来修饰类的非static成员函数,在函数的声明和定义中都要出现
  • 一个函数可以右const和&同时修饰,但是此时const必须加在&之前

const限定符

这个其实没啥说的,就是在把const加在参数列表之后。

  • 常量函数只能调用常量函数。
  • 常量对象只能调用其中的常量函数
<think>首先,用户的问题是:“我想了解const修饰结构体变量的作用”,这是在讨论C++中的const关键字,特别是它在修饰结构体变量时的作用。用户之前的查询是关于“find span”,但我已经处理了,现在是新的主题。 我需要参考用户提供的引用内容: - 引用[1]:讨论了const修饰符的使用,特别是修饰结构体指针引用外部const变量。它解释了const修饰变量只读、指针的不同情况(如const char *p 表示指向的内存不能变,char * const p 表示指针本身不能变)。 - 引用[2]:展示了C++中const在结构体变量中的使用,强调在指针传参时防止误操作,通过在参数前加const。 - 引用[3]:类似引用[2],用另一个例子说明const修饰结构体指针如何避免修改主函数实参。 - 引用[4]:比较了形参为const student *s(指针传递)const student s(值传递),并说明加const防止误操作。 系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式用$$...$$。 - 确保LaTeX语法正确。 - 用中文回答。 - 生成相关问题。 - 在引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题,尽量保证真实性。 我的回答结构: 1. **引言**:简要解释const的作用。 2. **修饰普通变量**:解释const修饰变量的作用。 3. **修饰结构体变量(值传递)**:解释当const修饰结构体本身作为值传递时。 4. **修饰结构体指针**:详细解释const在指针中的不同用法。 5. **实际应用**:结合引用中的例子,说明在函数参数中使用const的好处。 6. **总结**:强调重要性。 7. **相关问题**:生成相关问题。 确保添加引用: - 在解释时,自然地引用提供的引用内容。例如,当提到指针时引用[1],当提到结构体防止误操作时引用[2]或[3]。 生成相关问题: - 基于主题,生成2-3个相关问题。 最终输出格式: - 用中文。 - 包括代码块如果适用(引用中有代码示例)。 - 最后添加相关问题部分。 内容提纲: 1. **const修饰普通变量的作用**: - const使变量成为只读,不能修改其值。示例:const int a = 10; a = 20; // 错误[^1]。 2. **const修饰结构体变量**: - 当结构体作为值传递时,const struct S s 表示不能在函数内修改s的成员[^4]。 3. **const修饰结构体指针**: - const struct S* p:指针指向的内容不能修改(但指针本身可以修改)[^1][^3]。 - struct S* const p:指针本身不能修改(但指向的内容可以修改)[^1]。 - const struct S* const p:两者都不能修改[^1]。 - 在函数参数中使用,如void func(const struct S* s),防止误修改传入的数据[^2][^3][^4]。 4. **好处**: - 提高代码安全性可靠性,防止意外修改。 - 传递指针时节省内存(避免复制整个结构体),同时保护数据[^2][^4]。 5. **代码示例**: - 引用中的代码:例如从引用[3]或[4]复制示例代码。 相关问题: 1. const修饰符在C++中还有其他应用场景吗? 2. 如何在类中使用const修饰成员函数? 3. const指针结合使用时有哪些常见错误? </think>### `const`修饰结构体变量的作用 在C++中,`const`关键字用于声明常量,确保变量或对象的值在定义后不能被修改。这提高了代码的安全性、可靠性可读性,防止意外修改数据。下面我将逐步解释`const`修饰普通变量结构体的作用,并结合实际示例说明。 #### 1. **修饰普通变量的作用** - `const`修饰普通变量时,使其成为只读常量。一旦初始化,值就不能被改变。 - 语法示例:`const int a = 10;`,这里`a`被定义为常量,后续尝试赋值如`a = 20;`会编译错误[^1]。 - **作用**: - 防止误修改关键数据,增强代码安全性。 - 优化编译器行为,可能将常量直接嵌入代码以提高效率。 - 示例: ```cpp const double PI = 3.14159; // PI不能被修改 // PI = 3.14; // 错误:尝试修改常量 ``` #### 2. **修饰结构体变量(值传递)** - 当结构体通过值传递(复制副本)时,`const`修饰的结构体变量函数内部不能修改任何成员。 - 语法示例:`void func(const struct Student s);`,其中`s`是原结构体的副本。 - **作用**: - 保护传入的数据不被函数内部误操作修改。 - 缺点:如果结构体较大,值传递会复制整个对象,占用额外内存[^4]。 - 示例: ```cpp struct Student { string name; int age; }; void printStudent(const Student s) { // 值传递,s是副本 // s.age = 100; // 错误:const修饰的s不能修改成员 cout << "姓名:" << s.name << ",年龄:" << s.age << endl; } ``` - 这里`printStudent`函数接收`Student`的副本,`const`确保函数内无法修改成员值[^4]。 #### 3. **修饰结构体指针** - `const`修饰结构体指针时,有四种常见场景,保护指针本身或指向的内存: - **`const struct S* p`**(或等价于`struct S const* p`):指针指向的内容不能修改,但指针本身可以重新指向其他地址[^1][^3]。 - 示例:`const Student* p = &s;`,此时`p->age = 20;`会报错,但`p = &other;`合法。 - **`struct S* const p`**:指针本身不能修改(即不能指向新地址),但指向的内容可以修改[^1]。 - 示例:`Student* const p = &s;`,`p->age = 20;`合法,但`p = &other;`错误。 - **`const struct S* const p`**:双重保护,指针本身指向的内容都不能修改[^1]。 - 示例:`const Student* const p = &s;`,所有修改操作均非法。 - **在函数参数中使用**:传递结构体指针时加`const`,避免函数内部误修改原始数据。 - 语法:`void func(const struct Student* s);` - **作用**: - 防止函数内意外修改实参(如误赋值成员变量)。 - 节省内存(指针大小固定,避免复制整个结构体)[^2][^3][^4]。 - 示例(基于引用[3]): ```cpp void printStudent(const Student* s) { // 常量指针参数 // s->age = 100; // 错误:const保护指向内容不被修改 cout << "姓名:" << s->name << ",年龄:" << s->age << endl; } int main() { Student s1 = {"张三", 18}; printStudent(&s1); // 安全传递指针 } ``` - 这里`const Student* s`确保`printStudent`函数无法修改`s1`的原数据[^3]。 #### 4. **实际应用重要性** - **安全性**:在大型项目中,`const`防止关键数据(如配置参数或共享结构体)被意外篡改,减少bug[^2][^4]。 - **性能优化**:指针传递加`const`比值传递更高效,尤其对于大型结构体(避免复制开销)[^4]。 - **代码可读性**:明确标识只读数据,使代码意图更清晰。 - 通用规则:"跳过类型,从左往右看修饰符":例如`const char *p`修饰指向的内存,`char * const p`修饰指针本身[^1]。 #### 总结 - `const`修饰**普通变量**:创建只读常量。 - `const`修饰**结构体变量**(值传递):保护副本数据不被修改。 - `const`修饰**结构体指针**:灵活保护指针或指向内容,常用于函数参数提高安全性效率。 合理使用`const`是现代C++最佳实践的核心部分,能显著提升代码健壮性[^1][^2][^3][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值