C++基础:const关键字

本文介绍了C++中const关键字的使用,包括const修饰对象、const引用、const指针以及const在类成员函数中的应用。重点讲解了const如何保证对象在初始化后不可修改,以及在指针和成员函数中的细节,强调了底层const和顶层const的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在编程的时候,我们有时候希望定义这么一个对象:其内容在初始化后不能再修改。这个时候const关键字就排上用场了。

当一个对象被const修饰后,这个对象的内容在初始化后,再也无法被修改。值得注意的是,const修饰的对象必须要初始化。

可以看看以下面的例子:

int a = 10;         //a是常量
//const int i;      //编译器报错,因为const修饰的对象必须进行初始化(赋值)
const int j = 2;    //正确 
const int k = a;    //正确
a = j;              //正确
//j = 3;            //编译器报错,const修饰的对象内容无法被修改 

const和引用

我们看这个例子:

double a = 1.0; 
//int &b = a;       //报错,编译器不允许用const修饰的int引用double类型
const int &c = a;   //正确
a = 10.0;
cout << c << endl;  //输出:1

const int d = 100;
int e = &d;         //报错,编译器不允许引用const常量

这里定义的对象c之所以能够通过编译,是因为编译器让c引用了一个临时的变量,形式如下面的代码:

const int temp = a;
const int& c = temp;

这么一来,修改a的值后,输出c会发现c还是之前的1,并没有随着变量a的改变而改变。

const和指针

(1)const放在指针声明的最前面,表示指针指向的地址内容不可修改,譬如下面这个例子:

const int a = 10;
//int *b = &a;      //报错,因为编译器防止指针b指向a的地址后,通过b修改a的值
const int *b = &a;  //正确
*b = 5;             //报错,因为const修饰指针,表示指针指向的地址内容不可修改
const int *c;       //正确,const修饰指针,指针可以不初始化,参考一行
c = &a;             //正确,指针的值可以变,但指向的地址内容不能用这个指针修改
int *b = c;         //报错,这是底层const的限制,拷入和拷出对象必须具备相同的底层const资格

注:在C++ Primer中,形如const int *c,这种表示指针所指对象内容无法修改的const修饰,称为底层const (low-level const),此外本文提到的其他const都是顶层const(top-level const)。

(2)const的位置放在 * 号的后面,表示指针的值不能修改,例子如下:

int a = 10;
int *const b = &a;              //正确,声明一个b指针,受到const限制,指针b的值无法修改
*b = 5;                         //正确,修改指针b指向地址的内容
std::cout << a << std::endl;    //输出:5,因为通过指针c修改了a的值

//int *const c;                 //报错,指针c被const修饰,必须进行初始化(赋值)
int *const c = b;               //报错,指针c被const修饰,必须进行初始化(赋值)
//c = &a;                       //报错,指针由const修饰,值不可修改

const int *const d = &a;         //正确,即是顶层const,又是底层const

const和类成员函数:

再深入一些,看看下面这个例子:

class Student
{
public:
    Student() { age = 10; }
    int getAge() { return age; }
private:
    int age;
};

int main()
{
    Student stu1;
    const Student stu2;
    int age1 = stu1.getAge();  //正确
    int age2 = stu2.getAge();  //报错,错误为: [Error] passing 'const Student' as 'this' argument of 'int Student::getAge()' discards qualifiers [-fpermissive]
    return 0;
}

我们在调用getAge函数的时候明明没有修改Student类的成员,但是编译器为什么会报错呢?这里有两个关键点:

1、在调用Student对象(譬如stu1、stu2)的getAge函数时,编译器会隐式地传入这个对象的指针。为什么呢?毕竟Student的每个对象都有可能使用getAget函数,而编译器不会为每个对象分配一段getAge函数的代码,因此,每个Student对象其实是调用同一段getAge函数代码的。函数内那怎么直到调用者是哪个对象?应该返回哪对象的age成员值呢?其实,编译器在调用该函数时,会隐式地传入这个对象的指针(就像python类成员函数传入self参数一样),函数通过这个指针来访问该对象的成员age。这个跟我们写代码时用的this指针访问类内部成员一样,而这个隐式传入的指针却没有用const来修饰。我们可以按照下面的代码来理解:

//编译器隐式传入指针类似于(伪代码)
int Student::getAge(Student *const this) { return this->age; }
//编译器调用stu1的getAge函数类似于(伪代码)
int age1 = Student::getAge(&stu1);

2、在上述代码中,我们使用了const修饰了对象stu2,因此,stu2是不可修改的。然而stu2的地址传入getAge函数后,函数内部却可以修改stu2的成员age的值(虽然这个例子中没有这么做),编译为了防止这种情况发生才会报错。

//针对const对象这种情况,隐式传入的指针类型应该为const Student *const,伪代码:
int Student::getAge(const Student *const this) { return this->age; }

以上两点即是报错的原因。然而这个指针的传入又是隐式的,没有参数列表,应该怎样修改才能使得编译通过呢?很简单,只要在getAget函数的参数列表后添加一个const关键字即可,表明隐式传入的指针是const的,不可用其修改成员变量的值,代码如下:

class Student
{
public:
    Student() { age = 10; }
    int getAge() const { return age; }         //这里添加了const
private:
    int age;
};

int main()
{
    Student stu1;
    const Student stu2;
    int age1 = stu1.getAge();  //正确
    int age2 = stu2.getAge();  //正确
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值