在探讨const成员函数之前,我们先看const的几个基本用法:
一、 const 修饰变量
const在C++中用于修饰变量,表示该变量的值在初始化后不可被修改,即它是一个常量。使用 const 可以提高代码的可读性和安全性,因为它明确指出了哪些变量在程序中是不应该被修改的。
二、 const 修饰引用
const修饰引用,表示引用的对象不能被修改。在函数参数中使用 const 引用,可以避免不必要的对象拷贝,同时保证函数内部不会修改引用的对象。
void func(const int& x)
{
// x = 10; // 这会导致编译错误,因为 x 是一个对常量的引用
}
三、 const 修饰指针
int d1,d2,d3,d4;
const int* p1; //修饰的是*p1指向的对象
//*p1=1;//不行,因为*p1是const的
//p1=&d1;//可以
int const* p2; //修饰的是*p2指向的对象
//*p2=2;//不行,因为*p2是const的
//p2=&d2;//可以
int* const p3; //修饰的是p3指向的内容(p3指向的地址)
//*p3=3;//可以
//p3=&d3;//不行,因为p3是const的
const int* const p4;//既修饰*p4指向的对象,又修饰p4指向的内容
//*p4=4;//不行,因为*p4是const的
//p4=&d4;//不行,因为p4是const的
1. const int* p1
p1 是一个指针,它指向一个 const int 类型的对象。即p1指向的对象(*p1)是const的,这意味着你不能通过 p1 修改它所指向的 int 对象的内容。但是,可以改变 p1 本身的值,让它指向另一个 int 对象(或 const int 对象)。
2. int const* p2
与第一行完全相同。在C++中,const 关键字的位置在类型名和*之间或之后是等价的,所以 int const* 和 const int* 都表示指针指向的对象是 const 的。p2 同样不能用来修改它所指向对象的内容,但 p2 本身的值(即它所指向的地址)是可以改变的。
3. int* const p3
p3 是一个 const 指针,它指向一个 int 类型的对象。即p3指针本身是const的,这意味着 p3 指向的地址是固定的,你不能改变 p3 让它指向另一个对象。但是,可以通过 p3 修改它所指向的 int 对象的内容(除非该对象本身也是 const 的)。
4.const int* const p4
p4是一个const指针,它指向一个const int类型的对象。即p4指向的对象是const的,p4指针本身也是const的,这意呀着既不能改变指针的指向,也不能通过该指针修改对象的内容。
四、 const 成员函数
将const修饰的“成员函数”称为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
- 作用:const成员函数保证在函数体内不会修改任何成员变量。(除非这些成员变量被声明为mutable)
- 调用规则:
请思考下面的几个问题:
1. const对象可以调用非const成员函数吗?
不可以。const修饰的对象不能被修改,保证其在整个生命周期内都是不可变的,而非const成员函数可以自由的修改对象的任何成员变量,如果允许const对象调用非const成员函数,那么就会违反const对象的不可变性保证,从而导致未定义行为,所以const对象不能调用非const成员函数。
2. 非const对象可以调用const成员函数吗?
可以。非const对象可以被修改,而const成员函数承诺不会修改对象的任何成员变量,非const对象调用const成员函数,实际上是以一个更受限制的方式(即不修改对象状态)来访问对象的共能。这是完全合法的。
代码举例:
- 对象调用const成员函数
//对象调const成员函数
class Date
{
public:
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
void Print1() //void Print(Date* this)
{
// 非const成员函数,可以修改成员变量
_year=10;
cout << _year << "-" << _month << "-" << _day << endl;
}
void Print2()const //void Print(const Date* this)
{
// const成员函数,保证不会修改成员变量
//_year=10; //报错,const保护了*this
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024, 7, 16);
const Date d2(2024, 7, 16);
d1.Print2();//可以。非const对象调用const成员函数,权限缩小
d2.Print1();//不行。const对象调用非const成员函数,权限放大
return 0;
}
3. const成员函数内可以调用其它的非const成员函数吗?
不可以。const成员函数承诺在函数体内不会修改任何成员变量,而非const成员函数在函数体内可以修改成员变量,这就存在了逻辑上的矛盾:一个承诺不会修改成员变量的函数内正在调用一个可能修改成员变量的函数,这将违反const成员函数的承诺,因为编译器无法确定非const成员函数是否会修改对象的状态。因此,编译器会阻止这种调用,以确保const成员函数的正确性。
4. 非const成员函数内可以调用其它的const成员函数吗?
可以。const成员函数保证在函数体内不会修改对象任何成员变量,因此非const成员函数调用它是完全安全的。
代码举例:
- 成员函数调用const成员函数
//成员函数调用const成员函数
class Display
{
public:
void f1() //void Date* this
{
f2();//可以。非const成员函数调用const成员函数,权限缩小。
}
void f2() const //const Date* this
{
}
void f3()//void Date* this
{
}
void f4() const //const Date* this
{
f3(); //不行。const成员函数调用非const成员函数,权限放大。
}
};
通俗的来讲,非const修饰的对象和成员函数具有可读可写的权限,而const修饰的对象和成员函数具有只读的权限。权限只能缩小,不能放大!
权限和安全性
- 权限缩小:从非const成员函数到const成员函数的调用是权限的缩小(从可读可写到只读),这是安全的。
- 权限放大:从const成员函数到非const成员函数的调用是权限的放大(从只读到可读可写),这可能导致不可预测的行为,因此不被允许。
五、 总结:
const在C++中不仅仅是用来声明常量的。它在类设计中扮演着至关重要的角色,通过确保成员函数不会修改对象的状态,提高了代码的安全性和可维护性。在编写类时,始终考虑const正确性,并尽量将不需要修改成员变量的函数声明为const,这将有助于编写更清晰、更可靠的C++代码。