C++ 之 常量成员函数

时间:2022-11-10 20:19:45

  常量成员函数 (const member function), 可读取类中的数据成员,但不能修改

1  声明

1.1  const 关键字  

  参数列表后,加 const 关键字,声明为常量成员函数,表明其不被允许修改类的数据成员

  下面的类,以年、月、日的形式来表示日期 (注意:年月日的声明顺序)

class Date {
public:
int year() const;
int month() const { return m; }
int day() const { return d; }
void add_year(int n); // add n years
private:
int y, m, d;
};

  1) 如果常量成员函数,企图修改类的数据成员,则编译器会报错

// error : attempt to change member value in const function
int Date::year() const
{
return ++y;
}

  2) 如果在类外面,“定义” 常量成员函数 ( “定义” = 实现,即 implementation),则 const 关键字不可省略

// error : const missing in member function type
int Date::year()
{
return y;
}

1.2  C++ 陷阱

  类中成员变量的声明顺序,决定了成员变量的初始化顺序。假设 Date 类中的构造函数为:

public:
Date() : y(
2016), m(9), d(22) {}

  此时,类中的成员函数,在类中的声明顺序 = 构造函数初始化列表顺序,故 y, m, d 都能被顺利的初始化为对应的值。

  而当成员变量,在类中的声明顺序 ≠ 构造函数初始化列表顺序 时,

public:
Date() : y(
2016), d(22), m(d-13) {}

  根据成员变量的声明顺序,y 首先被初始化为 2016,然后再初始化 m,但由于 d 并未被初始化,所以 m 的值是随机的,最后初始化 d 为 22

  这是因为,类的成员变量在初始化时,其初始化的顺序只与声明顺序有关,而与在初始化列表中的顺序无关。

 

2  调用

  一个常量成员函数,可以被 const 和 non-const 类对象调用; 而非常量成员函数,例如 add_year(),则只能被 non-const 型类对象调用。

void  Date::add_year(int n)
{
y
+= n;
}

  调用函数如下:

void f(Date& d, const Date& cd)
{
int i = d.year(); // OK
d.add_year(1); // OK
int j = cd.year(); // OK
cd.add_year(1); // error
}

  此时,const 修饰函数形参,是 “接口” 的常用指定形式, 这样 数据 可以传递给 函数 而 本身不被修改

  C++ 中的类型转换 const_cast,可以移除对象的 const 属性,具体使用为: const_cast<T>(expression)

  则上例中,要使 const 型类对象,调用类的 non-const 成员函数,可修改代码如下:

void f(Date& d, const Date& cd)
{
int j = cd.year(); // OK
const_cast<Date&>(cd).add_year(1);
}

  这种做法虽然是可以的,但它破坏了使用 const 来指定 “接口“ 的本意,并不推荐。


3  解释

  this 指针 默认是指向 non-const 型类对象的 const 型,因此,不能将 this 指针和 const 型类对象绑定,即 const 类对象无法调用类的成员函数

// 默认的 this 指针,指向 non-const 类对象
Date * const this;

   在成员函数声明的参数列表后加 const 后缀,表明其 this 指针指向 const 型类对象,如此, const 型类对象便可以调用常量成员函数了

// 常量成员函数中的 this 指针,指向 const 类对象
const Date * const this;

  

小结:

1) 类成员函数声明中的 const 后缀,表明其 this 指针指向 const 型类对象,因此该 const 类对象,可以调用常量成员函数 (const member function)

2) 一个成员函数,如果对数据成员只涉及读操作,而不进行修改操作,则尽可能声明为常量成员函数

 

参考资料:

 <C++ Programming Language_4th> ch 16.2.9.1

 <C++ Primer_5th> ch 7.1.2

 <Effective C++_3rd> Item 3, item 27

 <More Effective C++> Item 2

 <剑指 offer> 第 7 章