Effective C++ 笔记

1 让自己习惯c++


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

可以将c++ 视为一个由相关语言组成的联邦而非单一语言。在某个次语言中,各种守则与通例都倾向简单、直观易懂、并且容易记住。然而当你从一个次语言移往另一个次语言,守则可能改变。为了理解c++,你必须认识其主要的次语言。幸运的是总只有四个:

  • C.
  • Object-Oriented C++.
  • Template C++.
  • STL.

请记住:

  • c++ 高效编程守则视状况而变化,取决于你使用 c++ 的哪一部分。

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

#define ASPECT_RATIO 1.653

const double AspectRatio = 1.653;
// 好处:1. 会被编译器识别,进入记号表(symbol table)
//       2. 使用常量比使用 #define 导致较小量的码


class GamePlayer {
    enum { NumTurns = 5 };

    int scores[NumTurns];
};
// 认识 enum hack : 1. 其行为比较像 #define 而非 const
// 2.实用主义。它是模板元编程的基础技术。

// 以 a 和 b 的较大值调用函数f
#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 被累加一次

// template inline
template<typename T>
inline void callWithMax(const T& a, const T& b)
{
    f(a > b ? a : b);
}

// #include 仍然是必需品,#ifdef/#ifndef 也继续扮演控制编译的重要角色。

请记住:

  • 对于单纯常量,最好以 const 对象或 enums 替换 #defines.
  • 对于形似函数的宏(macros),最好改用 inline 函数替换 #defines.

条款 03 尽可能使用 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

// f1 和 f2 等同
void f1(const Widget *pw);
or
void f2(Widget const *pw);

// STL 迭代器以指针塑模出来,所以迭代器的作用像个 T* 指针
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin(); //iter作用像个 T* const

std::vector<int>::const_iterator cIter = vec.begin(); //CIter作用像个 const T*

// const 用于函数声明式
class Rational { ... };
const Rational operator* (const Rational &lhs, const Rational &rhs);

Rational a, b, c;
if (a * b = c) ... // 其实想做一个比较动作!
// 将 operator* 的回传值声明为 const 可以预防那个“没意思的赋值动作”


// 事实: 两个成员函数如果只是常量性(constness)不同,可以被重载。
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;
}; 

// bitwise constness(又称 physical constness) : 成员函数只有在不更改对象之任何成员变量(static 除外)时才可以说是 const。
// logical constness : 一个const 成员函数可以修改它所处的对象内的某些bits,但只有在客户端
// 侦测不出的情况才得如此。
class CTextBlock {
public:
    ...
    std::size_t length() const;
private:
    char *pText;
    mutable std::size_t textLength;    // mutable(可变的)释放掉non-static成员变量
    mutable bool lengthIsValid;        // 的bitwise constness 约束
};

std::size_t CTextBlock::length() const
{
    if (!lengthIsValid) {
        textLength = std::strlen(pText);
        lengthIsValid = true;    
    }
    return textLength;
}

// const 和 non-const 成员函数中的避免重复
class TextBlock {
public:
    ...
    const char & operator[](std::size_t position) const
    {
        ...
        return text[position];
    }
    char & operator[](std::size_t position)
    {
        return const_cast<char&>(
            static_cast<const TextBlock&>(*this)[position]
        );
    }
};

请记住:

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

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

// 永远在使用对象之前先将它初始化。
// 对于无任何成员的内置类型,你必须手工完成此事。

int x = 0;                                // 对int 进行手工初始化
const char *text = "A C-style string";    // 对指针进行手工初始化

double d;
std::cin >> d;                            // 以读取input stream 的方式完成初始化

// 对于内置类型以外的任何东西,初始化的责任落在构造函数身上。
class PhoneNumber { ... };
class ABEntry {    // ABEntry = "Address Book Entry"
public:
    ABEntry();
    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::ABEntry(const std::string &name, const std::string &address,
        const std::list<PhoneNumber> &phones)
:theName(name), theAddress(address), thePhone(phones), numTimesConsulted(0)
{}

// 无参构造函数
ABEntry::ABEntry()
:theName(), theAddress(), thePhone(), numTimesConsulted(0) //numTimesConsulted显示初始化为0
{}

// 1. 小心地将"内置型成员变量"明确地加以初始化
// 2. 构造函数运用“成员初值列”初始化 base classes 和 成员变量
// 3. 不同编译单元内定义之 non-local static 对象的初始化次序
class FileSystem { ... };
FileSystem & tfs()
{
    static FileSystem fs;
    return fs;
}
class Directory { ... };
Directory::Directory( params )
{
    ...
    std::size_t disks = tfs().numDisks(); // reference to tfs 改为 tfs()
    ...
}
Directory& tmpDir()
{
    static Directory td;
    return td;
}

请记住:

  • 为内置型对象进行手工初始化,因为 C++ 不保证初始化它们
  • 构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class 中的声明次序相同
  • 为免除“跨编译单元之初始化次序”问题,请以 local static 对象替换 non-local static 对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值