Effective C++ 改善程序与设计的55个具体做法

Effective C++ 改善程序与设计的55个具体做法,第1张

Effective C++ 改善程序与设计的55个具体做法

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

将C++视为一个由相关语言组成的联邦,而非单一语言。即视为以下4个次语言组成的联邦:

(1)C:区块、语句、预处理器、内置数据类型、数组、指针等统统来自C。

(2)Obiect-Oriented C++:classes(包括构造函数和析构函数)、封装、继承、多态、虚函数(动态绑定)…等等

(3)Templates C++:泛型编程。

(4)STL:是个templates程序库。

条款02:尽量以const、emum、inline替换#define
请记住:

    对于单纯常量,最好以const对象或enums替换#define。对于形似函数的宏,最好改用inline函数替换#define。比如这种:
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))

1、用const 常量 替换#define有2个好处:

(1)当出现编译错误时,方便调试,因为#define会在预处理时进行替换。

(2)对浮点常量而言,使用常量可能比使用#define导致较小量的码。

2、#define不能够用来定义class专属常量,也不能够提供任何封装性,而const成员变量是可以被封装的。
3、一个属于枚举类型的数值,可权充ints被使用。

class GamePlayer
{
private:
    enum {NumTurns = 5};
    int scores[NumTurns];
    ...
};

条款03:尽可能使用const
请记住:

    将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性(conceptual constness)。当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。

1、const用于指针
如果关键字const出现在星号左边,表示被指物是常量;
如果出现在
右边,表示指针自身是常量;
如果出现在*号两边,表示被指物指针两者都是常量。

char greeting[] = "Hello;
char* p = greeting;             //non-const pointer, 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

2、const用于迭代器

std::vector vec;
...
const std::vector::iterator iter = vec.begin(); //iter的作用像个T* const
*iter = 10; //没问题,改变iter所指物
++iter; //错误!iter是const
std::vector::const_iterator cIter = vec.begin(); cIter的作用像个const T*
*cIter = 10; //错误!*cIter是const
++cIter;  //没问题,改变cIter。

3、令函数返回常量值

const fun();

4、const成员函数
将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上。
两个成员函数如果只是常量性不同,可以被重载。
const成员函数不可以更改对象内任何non-static成员变量。

class CTextBlock
{
public:
    ...
    std::size_t length() const;
private:
    char* pText;
    std::size_t textLength;
    bool lengthIsValid;  
}
std::size_t CTextBlock::length() const
{
    if(!lengthIsValid)
    {
        textLength = std::strlen(pText);//错误!在const成员函数内不能
        lengthIsValid = true;//赋值给textLength和lengthIsValid。
    }
    return textLength;
}

解决办法:利用C++的一个与const相关的摆动场:mutable(可变的)。mutable释放掉non-static成员变量的bitwise constness约束。

在这里插入代码class CTextBlock
{
public:
    ...
    std::size_t length() const;
private:
    char* pText;
    mutable std::size_t textLength;//这些成员变量可能总是会被更改,即使在const
    mutable bool lengthIsValid;  //成员函数内
}
std::size_t CTextBlock::length() const
{
    if(!lengthIsValid)
    {
        textLength = std::strlen(pText);//现在,可以这样,
        lengthIsValid = true;//也可以这样
    }
    return textLength;
}片

5、在const和non-const成员函数中避免重复
比如:

class TextBlock
{
public:
    ...
    const char& operator[] (std::size_t position) const
    {
        ... //边界检验
        ... //志记数据访问
        ... //检验数据完整性
        return text[position];
    }
    char& operator[] (std::size_t position)
    {
        ... //边界检验
        ... //志记数据访问
        ... //检验数据完整性
        return text[position];
    }
private:
    std::string text;
};

为了避免代码重复,利用2次转型(第1次:为*this添加const,这使接下来调用openator[ ]时,得以调用const版本。第2次,从const operator[]的返回值中移除const。),将上面代码修改为:

class TextBlock
{
public:
    ...
    const char& operator[] (std::size_t position) const  //一如既往
    {
        ... //边界检验
        ... //志记数据访问
        ... //检验数据完整性
        return text[position];
    }
    char& operator[] (std::size_t position) //现在只调用cosnt op[]
    {
        return const_cast(static_cast(*this)[position]); 
    }
...
};

条款04:确定对象被使用前已先被初始化
请记住:

    为内置型对象进行手工初始化,因为C++不保证初始化它们。构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值 *** 作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。为免除”跨编译单元之初始化次序“问题,请以local static对象替换non-local static对象。

1、对大多数类型而言,比起先调用default构造函数然后再调用copy assignment *** 作符,单只调用一次copy构造函数是比较高效的,有时甚至高效的多。
2、base classes早于其derived classes被初始化,而class的成员变量总是以其声明次序被初始化。

class PhoneNumber { ... };
class ABEntry //ABEntry = "Address Book Entry"
{
public:
    ABEntry(const std::string& name, const std::string& address, const std::list& phones);
private:
    std::string theName; //用初始化列表效率比赋值高。
    std::string theAddress;
    std::list thePhones;
    int numTimesConsulted;  //内置类型用成员初始化列表初始化或赋值,效率相同
};
ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list& phones)
{
    theName = name;  //这些都是赋值,而非初始化。因为对象的成员变量的初始化动作发生在
    theAddress = address;//进入构造函数本体之前。
    thePhones = phones;
    numTimesConsulted = 0;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/zaji/5711254.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存