Effective C++之const的用法

条款02 尽量以const,enum, inline替换#define

如把#define ASPECT_RATIO 1.653
替换为const double AspectRatio = 1.653

以常量替换#define特殊情况

1. 定义常量指针。

将指针(而不是指针所指之物)声明为const
例如,若在头文件内定义一个常量的(不变的)char*-based字符串,则必须写两次const

const char* const authorName = "Scott Meyers";
const std::string suthorName("Scott Meyers");//下面的更好一些
2. class专属常量

为了将常量的作用域限制于class内部,且此常量至多只有一份实体,必须使其成为class的一个static成员

class GamePlayer{
private:
	static const int NumTurns = 5;//常量声明式
	int scores[NumTurns];		//使用该常量
	...
};
//定义式,放在实现文件里,且由于声明时获得了初值,因此定义时不可再赋初值
const int GamePlayer::NUmTurns;

//旧编译器不允许static成员在其声明时获得初值
class GamePlayer{
private:
	static const double FudgeFactor;//常量声明式
	...
};
//定义式,放在实现文件里
const double GamePlayer::FudgeFactor = 1.35;

//当在编译时需要一个class常量值,则可用一个属于枚举类型代替
class GamePlayer{
private:
	enum{NumTurns = 5};//常量声明式
	int scores[NumTurns];		//使用该常量
	...
};
//注意:取const地址合法,enum地址和#define地址都不合法

条款03 尽可能使用const

1.const出现在*左边,表示被指物是常量

2.如果出现在*右边,表示指针自身是常量

3.如果出现在*两边,表示被指物和指针都是常量

4.如果被指物是常量,const写在类型之前或者类型之后、*之前都可

char greeting[] = "Hello";
char* p = greeting; //非常量指针,非常量数据
const char* p = greeting; //非常量指针,常量数据
char* const p = greeting; //常量指针,非常量数据
const char* const p = greeting; //常量指针,常量数据
//以下两者等价
void f1(const Widget* pw);
void f2(Widget const * pw);

STL迭代器(作用类似于T*指针)里的const

  1. 迭代器为const等同于声明指针为const,表明这个迭代器不得指向不同的东西,但它所指的东西的值是可以改变的
  2. 如果希望迭代器所指的东西的值不可改变,需要const_iterator
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();
*iter = 10;//正确
iter++;//错误
std::vector<int>::const_iterator citer = vec.begin();
*citer = 10;//错误
citer++;//正确

const成员函数

  1. 目的:确认该成员函数可用于const对象身上
  2. 两个成员函数如果只是常量性不同,则可以被重载
class TextBlock{
public:
	...
	const char& operator[](std::size_t position) const//用于const对象,返回一个const值
	{return text[position];}
	char& operator[](std::size_t position) //用于非const对象
	{return text[position];}
private:
	std::string text;	
};
TextBlock tb("Hello");
cout<<tb[0];//调用非const成员函数operator[]
const TextBlock ctb("Hello");
cout<<ctb[0];//调用const成员函数operator[]
void print(const TextBlock &ctb)
{
	std::cout<<ctb[0];//调用const TextBlock::operator[]
	...
}
cout<<tb[0];
tb[0] = 'x';//对于非const,两个操作都没问题
cout<<ctb[0];//正确
ctb[0] = 'x';//对于const,操作错误,错误原因:对一个const版的operator[]返回的一个const char& 施行赋值动作
//non-const operator[]返回类型是一个char的地址,不是char;
//如果对一个返回值是char而不是char&的赋值。则是错误的
  1. const成员函数不可更改对象内任何non-static成员变量
  2. 如果想在const函数里改变non-static,则应使用mutable释放掉non-static成员变量
class CTextBlock{
public:
	...
	std::size_t length() 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;
}
  1. 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复,const不能调用non-const
class TextBlock{
public:
	...
	const char& operator[](std::size_t position) const//用于const对象,返回一个const值
	{
	...
	...
	...
	return text[position];}
	char& operator[](std::size_t position) //用于非const对象
	{
		return 
		const_cast<char&>(//解除const operator[]的返回值中移除const
		 static_cast<const TextBlock&>(*this)//将*this从TextBlock&转型为const TextBlock&,使得调用operator[]时得以调用const版本
		 	[position]);
	}
private:
	std::string text;	
};

条款04 确定对象被使用前以先被初始化

1. 对于无任何成员的内置类型(int, bool等),必须手工完成初始化

2. 对于内置类型以外的,则靠构造函数:确保每一个构造函数都将对象的每一个成员初始化

初始化和赋值

class PhoneNumber{...};
class ABEntry{
public:
	ABEntry(const std::string& name, const atd::string& address, 
			const std::list<PhoneNumber>& phones);
private:
	std::string theName;
	std::string theAddress;
	std::list<PhoneNumber> thePhones;
	int numTimesCounsulted;.
};
ABEntry::ABEntry(const std::string& name, const atd::string& address, 
			const std::list<PhoneNumber>& phones)
{
	theName = name;
	theAddress = address;
	thePhones = phones;
	numTimesCounsulted = 0;//赋值,而不是初始化
	//首先调用default构造函数为theName,theAddress,thePhones设新值,然后立刻再对它们赋予新值 
}
//使用成员初始列替换赋值动作
ABEntry::ABEntry(const std::string& name, const atd::string& address, 
			const std::list<PhoneNumber>& phones)
			:theName(name),
			theAddress(address),
			thePhones(phones),
			numTimesCounsulted(0)//初始化
{
	
}
//使用default构造一个成员变量,可以使用成员初值列,只要指定无物作为初始化实参即可
ABEntry::ABEntry(const std::string& name, const atd::string& address, 
			const std::list<PhoneNumber>& phones)
			:theName(),
			theAddress(),
			thePhones(),
			numTimesCounsulted(0)//初始化
{
	
}

如果成员变量是const或references,就一定需要初值,不能被赋值

C++有着十分固定的 “成员初始化次序” ,次序总是相同,base classes更早于其derived classes被初始化,而class的成员变量总是以其声明次序被初始化

为免除“跨编译单元之初始化次序”问题,以local static对象替换non-local static对象

static对象:在函数内部的static对象被称为local static对象(因为它们对函数而言是local),其他的static对象称为non-local static对象
编译单元:单一源码文件加上其所含入的头文件(#include files)
至少有两个源码文件,每一个至少有一个non-local static对象(即该对象是global或者位于namespace作用域内,抑或在class内或file作用域内被声明为static
问题:如果某编译单元内的某个non-local static对象,它所用到的这个对象可能尚未被初始化
例如

class FileSystem{
publie:
	...
	std::size_t numDisks()const;
	...
};
extern FileSystem tfs;//位于golbal或者namespace作用域内
class Directory{
public:
	Directory(params);
	...
};
Directory::Directory(params)
{
	...
	std::size_t disks = tfs.numDisks();//使用tfs对象
	...
}
Directory tempDir(params);
//以上是错误的
//正确:将non-local换为local,使用函数返回的“指向static对象”的references而不再使用static对象自身
class FileSystem{
publie:
	...
	std::size_t numDisks()const;
	...
};
FileSystem& tfs()
{
	static FileSystem fs;//定义并初始化一个local static对象
	return fs;//返回它
}
class Directory{
public:
	Directory(params);
	...
};
Directory::Directory(params)
{
	...
	std::size_t disks = tfs().numDisks();//使用tfs对象
	...
}
Directory& tempDir()
{
	static Directory td;
	return td;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值