C和C++中const的区别
C中的const
C语言中的const修饰的是只读变量,本质还是变量,有自己的存储空间。const的含义是不能通过被修饰的变量名来改变这块存储空间的值,并不是说这块存储空间的值是不能改变的,但在如下示例中:
#include <iostream>
uaing namespace std;
int main()
{
const int a = 10;
int *p = (int *)&a;
*p = 20;
printf("a = %d\n", a);
return 0;
}
可以通过一个指针指向a所代表的内存空间,进而间接改变这块空间的值。另外,const修饰的仍然是变量,不可以用const修饰的变量作为数组长度去定义数组,如:
const int a = 10;
int arr[a];
这在某些编译器可能无法编译通过。
C++中的const
在C++中,const修饰的是真正的常量,而不是C中的只读变量。const常量会被编译器放入符号表中,符号表中存储的是一系列键值对,所以一般情况下,编译器不会为const常量分配内存空间。但是,当我们要对一个const常量进行取地址或者extern操作时编译器就会为该常量分配存储空间。并且,当我们使用const常量时,值是从符号表中或取得,而不是使用分配的存储空间的值。例如:
#include <iostream>
using namespace std;
int main()
{
const int a = 10;
int *p = (int *)&a;
*p = 20;
printf("&a = %p, p = %p\n", &a, p);
print("a = %d, *p = %d\n", a, *p);
return 0;
}
运行发现,&a与p输出的地址相同,但第二行的a和*p输出的值不一样,a输出还是10,*p输出20。a有自己的存储空间,但是在使用a的时候,并不是使用这个存储空间的值,而是从符号表中取出的值。
const与define
const与define的相同之处
C++中的const和define都可以用来定义常量,可以作为数组长度定义数组:
#define AAA 10
const int a = 20;
int arr1[AAA];
int arr2[a];
const与define的不同之处
const常量是由编译器处理的,提供类型检查和作用域检查,而define是有预处理器处理的,只是简单的文本替换。
const与指针结合
const int *p;
p所指向的空间的值不可修改,但可以更改p的值,指向其它地址空间。
int const *p;
同上
int* const p;
p所指向的空间可以的值可以修改,但p值不能修改,也就是不能指向其它空间。
注:在判断此类问题时,可以将数据类型int遮住,看const右边最靠近谁,如:
const *p;
const *p;
* const p;
const右边靠近星号(*),则指向空间的值不可修改,但p值可以修改
const右边靠近变量名,则p值不可修改,但所指向空间的值可以修改。
const与类
用在成员方法中修饰this指针
在类的成员方法中,会隐式添加一个this指针(ClassName* const this),作为函数的第一个参数(隐藏),这个指针指向当前操作的对象,this是一个常指针,函数内部可以通过this指针获取、修改成员变量的值,但很多情况下,我们只是获取成员变量的值,并不希望被修改成员变量,就需要对this指针做进一步的限制,将其改成const ClassName* const this,const成员函数也成为常成员函数:
DataType func(DataType name) const{}
const修饰成员变量
如果类中有const修饰的成员变量,初始化const成员变量的唯一方法是通过构造函数的参数初始化表,如:
class A
{
public:
A(int a, int b, int c):m_a(a), m_b(b)
{
m_c = c;
}
private:
int m_a;
const int m_b;
int m_c;
}
const与引用结合
引用在作为函数参数传递事务,形参与实参代表的是同一块内存,若不希望函数内部修改实参,在定义的时候最好定义成常引用。定义的方式:
const Type &name = var;
常引用的初始化方式有两种。
- 用一个普通变量去初始化常引用:
int a;
const int &b = a;
b是a的常引用,a和b代表的是同一块空间,但是不能通过b来修改a的值。
- 使用常量去初始化常引用
const int &a = 10;
常量10没有地址空间,当用常量对const引用初始化时,编译器会在内存中开辟一块空间,并用这个常量的值对新开辟的空间进行初始化,燃火将a作为这块空间的别名。
注:常量可以初始化常引用,但是不能初始化普通引用,如下语句是不合法的:
int &a = 10;
因此,如下写法也会报错:
int add(int &a, int &d);
add(10, 20);
const形参与函数重载
如果const修饰的是指针变量,可以通过判断它指向的是常量对象还是非常量对象,可以实现函数的重载,例如下面两个函数:
void add(int *a, int *b)
void add(const int *a, const int *b);
编译器可以通过判断实参是否常量来判断应该调用哪个函数。完整示例代码:
#include <iostream>
using namespace std;
void add(int *a, int *b)
{
cout << "(int *num)sum = " << *a + *b << endl;
}
void add(const int *a, const int *b)
{
cout << "(const int *num)sum = " << *a + *b << endl;
}
void add(int &a, int &b)
{
cout << "(int &num)sum = " << a + b << endl;
}
void add(const int &a, const int &b)
{
cout << "(const int &num)sum = " << a + b << endl;
}
int main()
{
const int a = 1;
int b = 2;
add(&a, &a);
add(&b, &b);
add(a, a);
add(b, b);
return 0;
}
运行结果:
(const int *num)sum = 2
(int *num)sum = 4
(const int &num)sum = 2
(int &num)sum = 4
当传入的实参是const常量对象时,编译器会优先调用const常量形参函数,当实参是非const常量对象时,编译器会优先调用非const常量形参函数。如果将两个带有const形参的函数去掉,也可以编译运行。
const作为函数返回值
const int func();
- 待整理
const int & func();
- 待整理