[Effective C++] 学习笔记之你所不知道的const用法

1. const自白

如果某个对象的值应该是保持不变(事实),你应该要把它说出,而我(const)就是帮你干这个事的。我可以告诉编译器和其他程序员该值保持不变,而编译器也会强制实施这项约束。

2. 指向常量和常指针

const在*号左边表示指向常量,const在 *号右边表示常指针。

char greetting[] = "Hello";
char* p = greetting;//指向非常量,非常指针
const char* p = greetting;//指向常量,非常指针
char* const p = greetting;//指向非常量,常指针
const char* const p = greetting;//指向常量,常指针

下面两种写法是一样的

void f(const Widget* w);
void f(Widget* const w);

3. const修饰的STL迭代器

声明迭代器为const,即声明一个T* const 指针;const_iterator是一个指向常量的指针。

std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();
*iter = 10;//没问题,改变iter所指的对象
iter++;//错误,iter为常指针
std::vector<int>::const_iterator cIter = vec.begin();
*cIter = 10;//错误,*cIter是一个常量
cIter++;//没问题,改变cIter的值

4. const最威用法

4.1 返回常数值

避免用户错误使用左值,又不失安全性和高效性。

class Rational{
...
const Rational operator* (const Rational& lhs, const Rational& rhs);
}

Rational a,b,c;
(a * b) = c;//错误用法,编译器报错,有助于查错 
if( (a * b) = c) ...//错误用法,编译器报错,有助于查错 

4.2 const成员函数

const成员函数,不能修改对象的数据成员,用const修饰成员函数,本质上是修饰隐藏的this指针。const成员函数和non-const成员函数是可以重载的。基于以下两方面,程序员应当尽量严谨地考虑是否用const修饰成员函数。

  1. 有利于理解class接口;
  2. 可以用来处理const对象;
class TextBlock{
	...
	const char& operator[] (std::size_t position) const{ 
		return text[position];
	}
	char& operator[] (std::size_t position) {
		return text[position];
	}
private:
	std::string text;
	int length;
}

//const对象大多用于pass-by-reference-to-const或是pass-by-pointer-to-const;
void print(const TextBlock& ctb){
	for(int i = 0; i < ctb.length; i++)
		std::cout<<ctb[i];
}

TextBlock tb("Hello");
const TextBlock ctb("World"):
std::cout<<tb[0];//没问题,调用char& operator[] (std::size_t position);
tb[0] = 'h';//没问题,因为返回类型为char&,如果是char则错误,而且return-by-value无法改变对象的数据成员。
print(ctb);//没问题,调用const char& operator[] (std::size_t position) const;
ctb[0] = 'w';//错误,因为返回类型为const char&,不能改变对象的数据成员。

4.3 bitwise const阵营 VS logical const阵营

bitwise const:常成员函数不可以更改no-static的成员变量;但存在缺陷,如下代码所示:

class CTextBlock{
public:
	...
	char& operator[] (std::size_t position) const {
		return pText[position];
	}
private:
	char* pText;
}const CTextBlock cctb("Hello");
char* pc = &cctb[0];
*pc = 'J';//通过[]操作符和指针更改了成员变量的值

另外在有些情况下,我们希望在客户端侦测不出来的情况下,常成员函数可以改变成员变量的值,但编译器坚持bitwise const;此时,我么可以使用mutable释放非静态成员的bitwise const约束。

class CTextBlock{
public:
	...
	std::size_t lenght() const;
private:
	char* pText;
	std::size_t textLength;
	bool lengthIsValid;
};
std::size_t CTextBlock::length() const{
	if(!lengthIsValid){
		textLength = std::strlen(pText);//编译不通过
		lengthIsValid = true;//编译不通过
	}
	return textLength;
}

//使用mutable
class CTextBlock{
public:
	...
	std::size_t lenght() const;
private:
	char* pText;
	mutable std::size_t textLength;
	mutable bool lengthIsValid;
};
std::size_t CTextBlock::length() const{
	if(!lengthIsValid){
		textLength = std::strlen(pText);//编译通过
		lengthIsValid = true;//编译通过
	}
	return textLength;
}

4.4 casting转型使得代码复用

class CTextBlock{
public:
	...
	char& operator[] (std::size_t position) const {
		...//相关复杂的操作
		...//如边界检查,标志数据访问等等
		return text[position];
	}
	char& operator[] (std::size_t position) {
		...//相关复杂的操作
		...//如边界检查,标志数据访问等等
		return text[position];
	}
private:
	std::String text;
}//调用操作符[] const并使用转型操作,避免代码重复
class CTextBlock{
public:
	...
	char& operator[] (std::size_t position) const {
		...//相关复杂的操作
		...//如边界检查,标志数据访问等等
		return text[position];
	}
	char& operator[] (std::size_t position) {
		return 
			const_cast<char&>(
				static_cast<const CTextBlock&>(*this)
					[position]
			);
	}
private:
	std::String text;
}

Reference

[1] Scott Meyers. 电子工业出版社. Effective C++中文版[M]. 2006.

发布了8 篇原创文章 · 获赞 7 · 访问量 438

猜你喜欢

转载自blog.csdn.net/sinat_38161291/article/details/104780743