学完Efficient c++ (01-03)

27 篇文章 0 订阅
24 篇文章 0 订阅

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

C++ 拥有多种不同的编程范式,而这些范式集成在一个语言中,使得 C++ 是一门即灵活又复杂的语言:

  1. 传统的面向过程 C:区块,语句,预处理器,内置数据类型,数组,指针。
  2. 面向对象的 C with Classes:类,封装,继承,多态,动态绑定(virtual函数)。
  3. 泛型编程 Template C++ 和堪称黑魔法的模板元编程(TMP)。
  4. C++ 标准库 STL(容器、迭代器、算法、适配器、空间配置器、算法)。

C++ 高效编程守则视情况而变化,例如,对内置类型而言,pass-by-value(按值传递)通常比pass-by-reference(按引用传递)高效,而对于用户自定义的类型,由于用户自定义的构造函数和析构函数的存在,pass-by-reference-const更好。

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

“宁可以编译器替换预处理器”——在编译器开始处理源码之前,记号名称就被预处理器移走了,,记号名称可能没进入记号表(symbol table)内。

在原书写成时 C++11 中的constexpr还未诞生,现在一般认为应当用constexpr定义编译时的常量来替代大部分的#define宏常量定义:

#define ASPECT_RATIO 1.653

替代为:

constexpr auto aspect_ratio = 1.653;

我们也可以将编译时的常量定义为类的静态成员:

class GamePlayer {
public:
    static constexpr auto numTurns = 5;
};

enum可以用于替代类内静态非整型的常量定义,并且在模板元编程中应用广泛(见条款 48):

class GamePlayer {
public:
    enum { numTurns = 5 };
};

enum和define行为比较像,而不像const常量。enum和define不能进行取地址,它们一样绝不会导致非必要的内存分配。

大部分#define宏常量应当用内联模板函数替代:

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

替代为:

template<typename T> //由于我们并不知道T是什么,所以采用pass-by-reference-to-const。
inline void CallWithMax(const T& a, const T& b) {
    f(a > b ? a : b);
}

需要注意的是,宏和函数的行为本身并不完全一致,宏只是简单的替换,并不涉及传参和复制。可以写出一个“class内的private inline函数”,而宏无法完成。

条款 3:尽可能使用 const

若你想让一个常量只读,那你应该明确说出它是const常量,对于指针来说,更是如此:

char greeting[] = "Hello";
char* p = greeting;                // 指针可修改,数据可修改
const char* p = greeting;          // 指针可修改,数据不可修改
char const* p = greeting;          // 指针可修改,数据不可修改
char* const p = greeting;          // 指针不可修改,数据可修改
const char* const p = greeting;    // 指针不可修改,数据不可修改

对于 STL 迭代器,分清使用const还是const_iterator

const std::vector<int>::iterator iter = vec.begin();    // 迭代器不可修改,数据可修改
std::vector<int>::const_iterator iter = vec.begin();    // 迭代器可修改,数据不可修改

面对函数声明时,如果你不想让一个函数的结果被无意义地当作左值,请使用const返回值:

const Rational operator*(const Rational& lhs, const Rational& rhs);

const成员函数:

const成员函数允许我们操控const对象,这在传递常引用时显得尤为重要:

class TextBlock {
public:
    const char& operator[](std::size_t position) const {    // const对象使用的重载
        return text[position];
    }

    char& operator[](std::size_t position) {                // non-const对象使用的重载
        return text[position];
    }

private:
    std::string text;
};

编译器对待const对象的态度通常是 bitwise constness(位常量性或物理常量性)(const成员函数不能修改对象内任何非静态成员变量),而我们在编写程序时通常采用 logical constness(逻辑常量性),这就意味着,在确保客户端不会察觉的情况下,我们认为const对象中的某些成员变量应当是允许被改变的,使用关键字mutable来标记这些成员变量:

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);    // 可以修改mutable成员变量
        lengthIsValid = true;               // 可以修改mutable成员变量
    }
    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) {
        //两次转型
        //(第一次:为*this添加const(为了调用operatorp[]的const版本);第二次从const operato[]的返回值中移除const。)
        return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
    }

private:
    std::string text;
};

需要注意的是,反向做法:令const版本调用non-const版本以避免重复——并不被建议,一般而言const版本的限制比non-const版本的限制更多,因此这样做会带来风险。

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值