const
一些const关键字常见的情况
# const修饰一般变量
const int a = 7;
int b = a; // 正确
a = 8; // 错误,不能改
2
3
即使用指针修改也是不可以的:
#include<iostream>
using namespace std;
int main(void)
{
const int a = 7;
int *p = (int*)&a;
*p = 8;
cout<<a; // 这里仍然输出a=7
system("pause");
return 0;
}
2
3
4
5
6
7
8
9
10
11
如果需要修改, 可以通过用volatile来修饰const,
#include<iostream>
using namespace std;
int main(void)
{
volatile const int a = 7;
int *p = (int*)&a;
*p = 8;
cout<<a; // 这里输出a=8
system("pause");
return 0;
}
2
3
4
5
6
7
8
9
10
11
# const修饰指针
Const 在指针的不同位置, 代表的含义也不一样, C++ primer里面会分为顶层const和底层const, 顶层const指指针本身是个const, 底层const指指针所指的对象是个const:
int* const x = &y; //顶层const: const指针 --> 指针指向的地址不能做修改
const int* x = &y; //底层const: const对象 --> 变量的指向内容不能做修改,
// y为一个const变量, 或者一个常规变量提升为一个const变量
const int* const x = &y; //Both两者都不能做修改
2
3
4
# const修饰引用
const引用就是指向const对象的引用,普通变量可以绑定到const 引用, 但是const变量不可以绑定到普通的引用上
int var1 = 1, var2 = 2;
int &a = 0; // 一般引用无法绑定到字面值常量
const int &b = 0; //const引用可以绑定字面常量值
int &c = var1 + var2; //错误,左值引用不可绑定右值, var1+var2为右值
const int &d = var1 + var2; //const引用可以绑定右值
2
3
4
5
# const修饰函数参数
传入const变量,一般这种情况不需要 const 修饰,因为函数会自动产生临时变量复制实参值。
传入const指针和const引用, 可以防止传入引用或指针改变原来的实参值
#include <iostream>
using namespace std;
void func(const int &a){
int &b = a;
b = 10;
}
int main(){
int a = 0;
func(a);
cout << a << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# const修饰函数返回值
const 修饰内置类型的返回值,修饰与不修饰返回值作用一样。
const 修饰自定义类型的作为返回值,此时**返回的值不能作为左值使用,既不能被赋值,也不能被修改。**这时候返回值为一个常量值,可以降低用户错误而造成的意外
#include <iostream>
#include <string>
using namespace std;
class A
{
private:
int _a;
string _str;
public:
A(int a, string str):_a(a), _str(str){}
const A operator+(const A &rhs);
friend ostream &operator<<(ostream &os, const A &a)
{
os << a._str << " : " << a._a;
return os;
}
};
const A A::operator+(const A &rhs)
{
return A(this->_a + rhs._a, this->_str + rhs._str);
}
int main(int argc, char *argv[])
{
A a1(1, "a1");
A a2(2, "a2");
A a3(3, "a3");
cout << (a1 + a2) << endl;
(a1 + a2) = a3; //错误, 但是如果去掉const,这里会有怎样的变化
return 0;
}
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
const 修饰返回的指针或者引用,是否返回一个指向 const 的指针,取决于我们想让使用者做什么。
# const修饰类成员变量
这个const关键字在修饰成员变量时,与上文提及的使用雷同,const变量主要在初始化列表中赋值,并且不能进行修改。
class object
{
…
const int value; //const变量不能被修改 (int const value也可以)
…
object (int x): value(x) { } ; // 构造函数初始化const变量
}
2
3
4
5
6
7
# const修饰类成员函数
const 修饰类成员函数,其目的是防止成员函数修改被调用对象的值,如果我们不想修改一个调用对象的值,所有的成员函数都应当声明为 const 成员函数。
**注意:**const 关键字不能与 static 关键字同时使用,因为 static 关键字修饰静态成员函数,静态成员函数不含有 this 指针,即不能实例化,const 成员函数必须具体到某一实例。
下面的 get_cm()const; 函数用到了 const 成员函数:
#include<iostream>
using namespace std;
class Test
{
public:
Test(){}
Test(int _m):_cm(_m){}
int get_cm()const
{
return _cm;
}
private:
int _cm;
};
void Cmf(const Test& _tt)
{
cout<<_tt.get_cm();
}
int main(void)
{
Test t(8);
Cmf(t);
system("pause");
return 0;
}
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
如果 get_cm() 去掉 const 修饰,则 Cmf 传递的 const _tt 即使没有改变对象的值,编译器也认为函数会改变对象的值,所以尽量将所有的不需要改变对象内容的函数都作为 const 成员函数。
如果有个成员函数想修改对象中的某一个成员怎么办?这时我们可以使用 mutable 关键字修饰这个成员,mutable 的意思也是易变的,容易改变的意思,被 mutable 关键字修饰的成员可以处于不断变化中,如下面的例子。
#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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
这里我们在 Kf() const 中通过 ++_ct; 修改 _ct 的值,但是通过 ++_cm 修改 _cm 则会报错。因为 ++_cm 没有用 mutable 修饰。
# const修饰类常量对象
常量对象所有成员默认添加了const修饰字,也就是所有的成员变量都不能进行修改。可以认为是关闭了写权限,所以const对象只能调用const成员函数,因为非const成员函数都有修改成员变量的权限。
#include <iostream>
using namespace std;
class A
{
public:
A(){cout << "non-const" << endl;}
void test() const{cout << "const" << endl;} //把这里的const去掉看看会发生什么
};
int main(int argc, char *argv[])
{
const A a1;
a1.test();
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# const和#define的区别
- 宏定义是在预编译阶段进行文本替换,而const是在编译阶段进行代码的语义约束。
- 由于二者所生效的阶段不同,所以能够起的作用也不同。const常量具有具体的类型,会在编译阶段进行类型检查,产生的代码显然会更加安全。(当然宏定义会更加灵活,可以定义代码或字符串,相对来讲如果要进行大量的宏替换,预编译时间会更长。)
- 宏定义很多时候是展开给立即数,多次替换需要多次分配内存,而const可以通过全局的符号表,仅保存一份copy就完事了。
# 使用const的一些建议
要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;
要避免最一般的赋值操作错误,如将const变量赋值;
在参数中使用const应该使用引用或指针,而不是一般的对象实例,原因同上;
const在成员函数中的三种用法(参数、返回值、函数)要很好的使用;
不要轻易的将函数的返回值类型定为const;
除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;