const 注意事项

const 的引用

  • 非常量引用不能指向一个常量对象
const int ci = 1024;
int &r2 = ci;  // 错误🙅
// 因为r2有可能改变ci的值
  • 初始化常量引用时允许使用任意表达式作为初值
int i = 42;
const int &r1 = i; // 正确,此时仅仅是不能通过r1修改i
i = 54; // 正确,i可以通过其它方式修改,此时r1也等于54

double dval = 3.14;
const int &ri = dval; // 正确
// 上面这段的过程如下
const int tmp = dval;
const int &ri = temp;

  • 函数传参时尽量使用常量引用
int add(int& a, int& b);
add(1, 2) // 错误!
int add_const(const int& a, const int& b);
add_const(1, 2) // 正确!

int use_add(const int& a, const int& b) {
	add(a, b) // 错误!
	add_const(a, b) // 正确!
}
  • 不能返回局部对象的引用
const std::string& foo() {
	std::string s;
	s = "hello world";
	return s; // 错误,引用是某一变量的别名,在函数返回后,变量都消失了,那能返回谁的引用呢?
}

const std::string foo() {
	std::string s;
	s = "hello world";
	return s;  // 正确,返回的是拷贝
}
std::string& foo(std::string& s) {  // 返回左值
	return s; 
}
const std::string& foo_const(const std::string& s) {  //返回左值,不过是const
	return s; 
}

std::string& foo_error(const std::string& s) { // 错误的函数!!!
	return s; 
}
std::string foo_right(const std::string& s) { // 正确的函数,返回右值
	return s; 
}

int main() {
  std::string s = "hello";
  foo(s) = "world"; // 引用返回的左值
  foo1(s) = "world"; // 错误!
  std::cout << s << std::endl;
  std::string& ss = foo_const(s); // 错误!非常量引用不能引用常量
  const std::string& ss = foo1(s);  // 正确
  std::string ss = foo_const(s);  // 正确
  std::cout << ss << std::endl;
  return 0;
}

const 成员

  • 常量对象和重载

this 指针是类中隐式定义的指向类类型的非常量版本的常量指针(始终指向类类型,但是不能被 const 对象使用)。

如果我们需要 const 对象使用成员函数,则需要尽可能将不会改变 this 所指对象但成员函数设置为 const 成员函数。而且,两个成员函数如果仅仅是常量性不同,则可以被重载。

例如:

class A {
  public:
    int& value() {
      std::cout << "This is normal version" << std::endl;
      return m_value;
    }
    const int& value() const {
      std::cout << "This is const version" << std::endl;
      return m_value;
    }
  private:
   int m_value = 2;
};

void print(const A& a) {  
  std::cout << a.value() << std::endl;  // 只能调用const版本
}

int main() {
  A a1;
  std::cout << a1.value() << std::endl;  // 默认使用了非const版本,但也可以使用const版本
  const A a2;
  std::cout << a2.value() << std::endl;  // 只能使用const版本
	print(a1);
  return 0;
}
// 打印结果
// This is normal version
// 2
// This is const version
// 2
// This is const version
// 2
  • 避免代码重复

以上的 value 函数的两个版本有着完全相同的函数内容,为了避免代码重复,我们可以使用类型转换:

const int& value() const {
  std::cout << "This is const version" << std::endl;
  return m_value;
}
int& value() {
  return
    const_cast<int&> (
      static_cast<const A&>(*this).value()
    );
}

注意⚠️:只能 non-const 版本调用 const 版本,不能反向操作!

也可以把这两个函数的公共部分定义成一个 private 的内联函数,这样不会增加开销也便于维护。

  • 使用 mutable

如果想在 const 成员函数内改变成员变量,需要声明成员变量为 mutable。

class A {
  public:
    const int& value() const {
      std::cout << "This is const version" << std::endl;
      use_time++;  // const函数内改变了成员变量
      return m_value;
    }
    int& value() {
      return
        const_cast<int&> (
          static_cast<const A&>(*this).value()
        );
    }
  private:
    int m_value = 2;
    mutable int use_time = 0;
    // int use_time = 0;  被const value()使用会出错!
};
  • 静态成员 static

静态成员为类的所有对象共享。

静态成员函数不能被声明为 const,因为它们不包含 this 指针。静态成员函数也不能与任何对象绑定。

  • copy assignment

如果 class 中含有 reference 成员和 const 成员,class 将拒绝为其生成 copy assignment 操作符。

作者: 公子小白

SOS团团员,非外星人、未来人、超能力者。。。

《const 注意事项》有一个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注