文章目录
条款01.尽量以const,enum,inline替换#define
这样子能够是编译器有较好的较错机制
常量定义
#define ASPECT_RATIO 1,653
通常替换为
const double AspectRatio = 1.653
定义常量指针
相对于 const char* const authorName = "Scott Meyers";
使用 const std::string authorName( "Scott Meyers");
class专属常量
将常量的作用域现定于class内,将它成为class的一个成员
class GamePlayer{
private:
static const int NumTurns = 5; //常量声明式
int scores[NumTurns]; //使用该常量
}
对于C++而言,所使用的东西都要有一个定义式。如果是个class专属常量又是static且为整数类(例如int,char,bool)可以无需提供定义式。
而定义时需要在实现文件(.cpp)实现
//该常量在声明的时候获得初值(5),定义时不可以再设初值。
const int GamPlayer::NumTurns;
而#define没有作用域,无法创建一个class专属常量,也无法提供封装性
当编译器不允许static整数型class常量完成“in class的初值设定”,可以改用所谓的“the enum hack”方法补偿。
class GamePlayer{
private:
enum{ NumTurns = 5 };
int scores{NumTurns};
}
内联函数替代宏函数
以一个比较a与b的较大值调用f函数为例
#define CALL_WITH_MAX(a,b) f( (a) > (b) ? (a) : b)
可以使用内联函数
template<typename T>
inline void callWithMax(const T& a,const T& b)
{
f(a > b ? a : b);
}
**因为callWithMax是一个真正的函数,遵守作用域和访问规则可以写一个“class内的private inline函数”**而宏函数这不可以。
条款03.尽可能的使用const
const允许你告诉编译器和其他程序员某值应该保持不变
类型变量const
char greeting[] = "hello";
char* p = greeting; //non-const poniter.non-const data
const char* p = greeting; //non-const pointer.const data
char* const p = greeting; //const pointer,non-const data
const char* const p = greeting //const pointer,const data
函数返回值为const类型
class Rational {...};
const Rational operator* (const Rational & lhs,const Rational& rhs);
可以防止出现如下的错误
Rational a,b,c;
...
if(a * b = c) //其实想做一个比较判断
const 成员函数
const的成员函数的目的如下
- 使class接口比较容易被理解
- 使“操作const对象”成为可能
两个函数的常量性不同就能被重载
举例说明
class TextBlock{
public:
...
const char& operator[](std::size_t position) const //operator[] for const 对象
{ return text[position];}
char& operator[](std::size_t position) //operator[] for non-const 对象
{ return text[position];}
private:
std::string text;
}
TextBlock的operator[]可被这么使用
TextBlock tb("Hello");
std::cout << tb[0]; //调用non-const TextBlock::operator[]
const TextBlock ctb("World");
std::cout << ctb[0]; //调用const TextBlock::operator[]
tb[0] = 'x'; //没问题,写一个non-const TextBlock
ctb[0] = 'x'; //错误!——写一个const TextBlock
const成员函数的实质是不更改类中的任何成员变量。然而在一个只有指针隶属于对象,更改了“指针所指物”的成员还是能通过编制器的认证
对于想在const成员函数中修改成员变量而不编译报错,可以在成员变量前增加前缀mutable。
在const和non-const成员函数中避免重复
一般const与non-const中除了返回的类型不同,代码的其它部分完全一致
为了避免代码的重复,以TextBlock类为例,op[]操作中还要进行边界检验,志记数据访问,检验数据完整性。
class TextBlock{
public:
...
const char& operator[](std::size_t position) const
{
... //边界检验
... //志记数据访问
... //检验数据完整性
return text[ position ];
}
char& operator[](std::size_t position) //现在调用const op[]
{
return
const_cast<char&>(static_cast<const TextBlock&>(*this)[position]); //调用const op[]
}
...
};
其中涉及两个类型转换
static_cast<const TextBlock&>(*this)[position]将类强制转换为const类型,避免函数op[]无线嵌套自身,const_cast<char&>(…)将返回的const char& 去掉引用。
条款04.确定对象被使用前已被初始化
对于内置的类型可以手工初始化
int x = 0;
const char* text = "A C-style string";
类的初始化
对于一个类要区分初始化和赋值操作,以电话本为例
class PhoneNumber { ... };
class ABEntry {
public:ABEntry(const std::string& name,const std::string& address, const std::list<PhoneNumber>& phones);
private:
std::string theName;
std::string theAddress;
std::list<PhoneNumber> thePhones;
int numTimesConsulted;
};
//赋值操作
ABEntry(const std::string& name,const std::string& address, const std::list<PhoneNumber>& phones)
{
theName = name;
theAddress = address;
thePhones = phones;
numTimesConsulted = 01;
}
//初始化
ABEntry(const std::string& name,const std::string& address, const std::list<PhoneNumber>& phones)
:theName(name),
theAddress(address),
thePhones(phones),
numTimeConsulted(0)
{} //构造函数本体不必有任何动作。
//ABEntry是一个无参构造函数,也可以使用默认构造一个成员变量
ABEntry::ABEntry()
:theName(), //调用theName的默认构造函数
theAddress(), //同上
thePhones(), //同上
numTimesConsulted(0) //将其显式初始化为0
{
}
相比于赋值操作,初始化效率更高。因此总是使用成员初值列。对于有多个构造函数的class,可以将"赋值表现像初始化一样好"的成员变量封装成私有函数供构造函数调用
初始化顺序
class的成员变量总是以其声明的方式被初始化化,与在成员初值中的次序出现无关。
对于全局定义的class,使用单例模式。
non-local-static对象
对于该类对象来说,在未初始化之前调用它会引发灾难性结果,一般使用单例模式来保持。