常成员变量、常成员函数与常对象
常成员变量
- 声明为常成员变量的成员变量,在对象被创建后就不能被修改
- 常成员变量必须在对象的构造函数初始化列表中赋值,不能在构造函数体中赋值
初始化列表的执行是在函数体执行之前就执行了的。上面这种写法和下面的写法类似,但下面的写法叫作函数体内赋值:
对于内置类型如int类型的成员变量,使用构造函数初始化列表来初始化和使用赋值语句来初始化其实差别并不大。但是,对于类类型的成员变量,使用初始化列表的方式初始化比使用赋值语句初始化效率更高(因为少调用了一次甚至几次该成员变量相关类的各种特殊成员函数,如构造函数等)
在C++中,对象的构造过程分为两个阶段:初始化阶段和构造函数体执行阶段。在初始化阶段,成员变量被初始化,包括非常成员变量和常成员变量。而构造函数体执行阶段则是在初始化阶段之后,用于执行用户自定义的构造逻辑。
由于常成员变量必须在对象创建后就不能被修改,所以必须在初始化阶段完成对常成员变量的初始化。而初始化列表提供了在初始化阶段对成员变量进行初始化的机会,因此常成员变量的初始化必须在构造函数初始化列表中完成。
- 常成员变量的值在对象生命周期内保持不变。
常成员函数
- 声明为常成员函数的成员函数不会修改对象的状态
- 在常成员函数中,不允许修改非常成员变量,也不允许调用非常成员函数
常对象
- 声明为常对象的对象在创建后就不能被修改
- 常对象只能调用常成员函数,不能调用非常成员函数,以确保对象的状态不能改变
- 常对象的所有成员变量都被视为常成员变量
总结
- 常成员变量通常用于声明对象的状态中不希望被修改的部分。
- 常成员函数用于表明该函数不会修改对象的状态,以便于在常对象上调用。
- 常对象是在声明时使用 const 关键字修饰的对象,它的状态和行为受到限制,只能调用常成员函数,并且不能修改成员变量。
mutable关键字
从上述定义概述中,我们可以知道,常成员函数是不能修改对象内的任何成员变量的值的,但是设计类成员变量的时候,假如确实遇到了需要在const结尾的成员函数中希望修改成员变量值的需求,怎么办呢?
也许有人会说,那就把函数末尾的const去掉,变成一个不以const结尾的成员函数。那这个时候可能面临上面曾提到过的另外一个问题——如果这个成员函数从const变成非const了,那么就不能被const对象调用了
所以,引入了mutable修饰符(关键字)来修饰一个成员变量。一个成员变量一旦被mutable所修饰,就表示这个成员变量永远处于可变状态,即使是在以const结尾的成员函数中。
如下所示,我们定义一段代码
class Human
{
public:Human(){};Human(std::string name):_name(name){};virtual void show(){std::cout<<"this is human!"<<std::endl;}std::string getName() const{return this->_name;}int getAge() const {return _age;}
private:std::string _name;int _age;
};
在上述代码的基础上,我们将一个getAge这个常成员函数修改一下
可以看到,编译器会自动报错,原因是因为在常成员函数中不能修改任何一个成员变量的值,这时如果我们必须要进行修改,就需要将成员变量声明为mutable的,即
mutable int _age;
这样,代码就不会报错,且满足我们的需求了。
完整代码如下:
class Human
{
public:Human(){};Human(std::string name):_name(name){};virtual void show(){std::cout<<"this is human!"<<std::endl;}std::string getName() const{return this->_name;}int getAge() const {return ++_age;}
private:std::string _name;mutable int _age;
};