条款1-3

条款01:视C++为一个语言联邦

将C++视为由四个次语言组成的联邦,每一个次语言都有自己的守则,跨越语言时,需要改变规则:

  • C:C++以C为基础。区块(blocks)、语句(statements)、预处理器(preprocessor)、内置数据类型(build_in data types)、指针(pointer)等统统来自C
  • Object-Oriented C++:这部分也是C with Classes 所诉求的:classes、封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定)
  • Template C++:这是C++泛型编程(generic programming)部分
  • STL:STL是个template程序库。它对容器(containers)、迭代器(iterators)、算法(algorithm)以及函数对象(function objects)的约束有极佳的紧密配合和协调

C++高效编程守则视状况而变化,取决于你使用那一部分


条款02:尽量以const、enmu、inline替换#define

#define ASPECT_RATIO 1.653

        记号名称ASPECT_RATIO可能从未被编译器看见;也许在编译器开始处理源代码之前它就被预处理器移走了。于是记号名称可能没有进入记号列表(symbol table)内。

        1.const:

        可以用一个常量替换上述的宏(#define):

const double AspectRatio=1.653

        作为语言常量,AspectRatio肯定会被编译器看见,然后进入记号表。

        以常量替换#define,有两种特殊情况:

  •   定义常量指针

        由于常量定义式通常放在头文件内,因而有必要将指针(不是指针所指之物)声明为const

  •  class专属常量

        为了将常量的作用域(scope)限制于class内,你必须让它成为class的一个成员(member);而为了确保此常量至多只有一个实体你必须让它成为一个static成员:

class GamePlayer{
private:
static const int NumTurns=5;   //常量声明式,类成员不能在定义时初始化
int score[NumTurns];
};

        通常C++要求你对你所使用的任何东西提供一个定义式,但如果它是一个class专属常量又是static且为整数类型(例如int、char、bool),则需要特殊处理——只要不取它们的地址,你可以声明并使用他们而无需提供定义式。反之,你就必须提供一个定义式,如下:

const int Gameplayer::NumTurns;       //NumTurns的定义,由于在声明时已获得初始值,因此无需再次赋值

        顺带一提,我们无法用#define创建一个class专属常量,因为#define并不重视作用域。一旦定义,一直有效(除非在某处被#undef)。这意味着#define不能定义class专属常量,也不能够提供任何封装性,即没有private#define。

        2.enum:
class GamePlayer{
private:
enum{NumTurns=5};   //常量声明式,类成员不能在定义时初始化
int score[NumTurns];
};
        从某些方面看,enum hack比较像#define而不像const
        3.inline:
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
        无论何时,当你写出这种宏,必须为宏中的所有实参加上小括号以避免不必要的麻烦。但纵使加上小括号人有不可预期的事情:
int a=5,b=0;
CALL_WITH_MAX(++a,b);        //a被累加两次
CALL_WITH_MAX(++a,b+10)      //a被累加一次
        在这里,调用f之前,a的递增次数竟取决于“它被拿来和谁比较”!
        解决这个问题的办法:
template<typename T> call_with_max(const T &a,const T &b)
{
f(a>b?a:b);
}
请记住:
对于单纯常量,最好以const对象或enums替换#define;
对于形似函数的宏(macros),最好改用inline函数替换#define;

条款03:尽可能使用const:

        const出现在*左侧,表示被指物是常量;如果出现在*右侧,表示指针自身是常量;如果出现在*两边,表示被指物和指针两者都是常量。

        如果被指物是常量,可以将关键字const写在类型之前或之后,两种写法意义相同:

const Widget *pw;
Widget const *pw;               //两者意义相同

        const最具威力的用法是面对函数声明时的应用,例如,令函数返回一个常量值可以降低因客户错误而造成意外:

class Rational{};
const Rational operater*(const Rational &lhs,const Rational &rhs);
Rational a,b;
if(a*b=c);    //其实是想做一个比较动作
        const成员函数:
        两个成员函数如果只是常量性(constness)不同,可以被重载:
class TexBlock{
public:
const char& operation[](std::size_t position)const
{return text[position];}    //operator[] for const 对象
char& operator[](std::size_t position)
{return text[position];}    //operator[] for non_const对象
private:
std::string text;
};
TextBlock tb("hello");
const TextBlock ctb("world");
std::cout<<tb[0];     //读一个non_const TextBlock
tb[0]='x';            //写一个non_const TextBlock
std::cout<<ctb[0];    //读一个const TextBlock
ctb[0]='x';           //错误!写一个const TextBlock
        请注意,non_const operator[ ]的返回类型是一个reference to char,不是char。如果operator[ ]只是返回一个char,下面的语句就无法通过编译:
tb[0]='x';
        因为如果函数返回值是一个内置类型,那么改动函数返回值从来就不合法。即使合法,C++以by value返回对象意味着改变的是tb.text的副本,而不是tb.text[0]自身。

        成员函数为const的意义
对于这个问题有两个流行概念:bitwise constnesslogical constness
        bitwise constness:成员函数只有在不更改对象之任何成员变量时才可以说是const
        这种观点的好处是很容易侦测违反点:编译器只需寻找成员变量的赋值动作即可。bitwise constness正是C++对常量性(constness)的定义,因此const函数不可以更改对象内任何 non_static成员变量
        但是许多函数不具备十足的const性质,但是能通过bitwise测试,如,一个更改指针所指物的成员函数虽然不能算const,但如果只有指针是隶属于对象,那么称此函数为bitwise const不会引发编译器异议,但导致反直观结果。
class CTextBlock{
public:
char& operation[](std::size_t position)const 
{return pText[position];}
private:
char *pText;
};
const CTextBlock cctb("hello");
char *pc=&cctb[0];
*pc='x';
operator[ ]代码并不更改pText,于是编译器为operation[ ]产出目标代码,所有编译器都认定它是bitwise const,但最终还是改变了对象的值,于是就出现了logical constness。
        logical constness:一个const成员函数可以修改所处理对象的某些bits,但只有在客户端侦测不出的情况下才得如此
class CTextBlock{
public:
std::size_t length()const;
private:
char *pText;
std::size_t text_length;
bool lengthIsValid;
};
std::size_t CTextBlock::length()const
{
if(!lengthIsValid)
{
text_length=std::strlen(pText);            //错误
lengthIsValid=true;                        //错误,在const成员函数内不能对其赋值
}
return textLength;
}
        这两笔数据的修改对const CTextBlock对象而言可以接受,但是编译器并不同意,这时可以利用C++与const相关的摆动场:mutable(可变的),释放掉non_static成员变量的bitwise constness约束:
class CTextBlock{
public:
std::size_t length()const;
private:
char *pText;
mutable std::size_t text_length;
mutable bool lengthIsValid;                 //这些成员总是会被改变,即使是在const成员函数内
};
std::size_t CTextBlock::length()const
{
if(!lengthIsValid)
{
text_length=std::strlen(pText);            
lengthIsValid=true;                        //编译通过
}
return textLength;
}
        在const和non_const成员函数中避免重复
class TextBlock{
public:
const char& operator[](std::size_t position)const
{
...    //bounds checking
...    //log access data
...    //verify data integrity
return text[position];
}
char& operator[](std::size_t position)
{
...    //bounds checking
...    //log access data
...    //verify data integrity
return text[position];
}
private:
std::string text;
};
        在这两个成员函数中,所执行的操作都相同,应该做的是实现operator[ ]的机能一次并使用它两次。即:令其中一个调用另一个。这促使我们使用常量性转除(casting away constness)
        一般而言,转型(casting)是一个糟糕的想法,详见条款27。
class TextBlock{
public:
const char& operator[](std::size_t position)const
{
...    //bounds checking
...    //log access data
...    //verify data integrity
return text[position];
}
char& operator[](std::size_t position)
{
return
      const_cast<char&>(                                   //将operator[]返回值的const转除
           static_cast<const TextBlock&>(*this)            //将*this(non_const TextBlock)转换为const
                                       [position]);        //调用const operator[]
}
private:
std::string text;
};
       这里有两个转型动作,首先将*this从其原始类型const TextBlock& 装换为const TextBlock&,第二次是从const operator[ ]的返回值中移除const。
        这是运用const operator[ ]实现出non_const版本,值得注意的是反向做法——令const版本调用non_const版本——不是应该做的事。记住, const成员函数承诺绝不更改其对象的逻辑状态(logical state),non-const成员函数没有这样的承诺
请记住:
        将某些东西声明为const可帮助编译器这测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
        编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)
        当const和non_const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值