Effective C++ 55个具体做法

条款20:宁已pass-by-reference-to-const替换pass-by-value

1.尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可避免切割问题。

class Window {
public:
    std::string name() const; // 返回窗口名称
    virtual void display() const; // 显示窗口和其内容
};

class WindowWithScrollBars : public Window {
public:
    virtual void display() const;
};

void printfNameAndDisplay(Window w) {   // 不正确!参数可能被切割
    std::cout << w.name();
    w.display();
}

当你调用上述接口并交给他一个WindowWithScrollBars对象

WindowWithScrollBars wwsb;
printfNameAndDisplay(wwsb);

参数会被构造成为一个Window对象,他是pass-by-value,因此在printfNameAndDisplay调用display总是Window::display()
解决切割问题的办法,就是以pass-by-reference-to-const的方式传递w

void printfNameAndDisplay(const Window& w) {   // 很好,参数不会被切割
    std::cout << w.name();
    w.display();
}

2.以上规则并不重点内容试用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较适当。

以上.

条款21:必须返回对象时,别妄想返回其reference

绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象。
当你必须在“返回一个reference和返回一个object之间抉择时,你的工作就是挑出行为正确的那个。让编译器去‘尽可能的降低成本’”。

以上.

条款22:将成员变量声明为private

切记将成员变量声明为private. 这可赋予客户访问数据的一致性,可细微划分访问控制,允许约束条件获得保证,并提供class作者以充分的实现弹性。
protected并不比public更具封装性。

以上.

条款23:宁以non-member、non-friend替换、member函数

这样做可以增加封装性、包裹弹性和机能扩充性。

namespace WebBrowesrStuff {
class WebBrowesr {
public:
    void clearCache();
    void clearHistory();
    void removeCooies();
};

void clearBrowser(WebBrowesr& wb) {  // 而不是WebBrowesr提供一个member函数
    wb.clearCache();
    wb.clearHistory();
    wb.removeCooies();
}
}

以上.

条款24:若所有参数皆需类型转换,请为此采用non-member函数

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);  // 构造函数可以不为explictit,允许int-to-Rational隐式转换
    int numerator() const;
    int denominator() const;
    const Rational operator* (const Rational& rhs) const;
};

Rational oneEighth(1, 8);
Rational oneHalf(1, 2);
Rational result = oneHalf * oneEighth; // 很好
result = result * oneEighth; // 很好
result = oneHalf * 2; // 很好 result = oneHalf.operator*(2);
result = 2 * oneHalf; // 错误!result = 2.operator*(oneHalf); 一目了然

换一种做法

const Rational operator* (const Rational& lhs, const Rational& rhs) const {
    return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
}
Rational oneFourth(1, 4);
Rational result;
result = oneFourth * 2; // 正确
result = 2 * oneFourth; // 正确

以上.


条款25:考虑写出一个不抛异常的swap函数

1.default swap 将两个对象的值彼此赋值给对方。缺省的情况下swap可由程序标准库提供的swap算法完成。

namespace std {
    template<typename T>
    void swap(T& a, T& b) {     // std::swap的典型实现
        T temp(a);              // 置换a和b的值
        a = b;
        b = temp;
    }
}

2.std::swaps特化版本
如果你希望交换的是这样的对象,pimpl手法(以指针指向一个对象,内含真正的数据)

class WidgetImpl {
public:

private:
    int a, b, c;                // 可能有许多数据
    std::vector<double> v;      // 意味复制时间很长
};

class Widget {
public:
    Widget(const Widget& rhs);
    Widget& operator=(const Widget& rhs) {  // 复制Widget时,复制其WidgetImpl
        *pImpl = *(rhs.pImpl);
    }
private:
    WidgetImpl* pImpl;
};

一旦要置换两个Widget的值,我们要做的其实只是置换其pImpl指针。而调用std::swap它不只复制三个Widget,还复制三个WidgetImpl对象。非常缺乏效率。
所以这样的做法更适合我们:

namespace std {
    template<>      // 表示这是std::swap的一个全特化版本
    void swap<Widget>(Widget& a, Widget& b) { // 表示这一特化版本针对“T是Widget设计”
        swap(a.pImpl, b.pImpl);
    }
}

这里要注意一点:通常我们不被允许改变std命名空间内的任何东西,但是可以为标准的templates制造特化版本。正如上面的例子。
3.member swap
但是通常当a和b想要访问的成员都是private,所以我们可以再改变上面的方法,把它实现为public member function

class Widget {
public:
    void swap(Widget& other) {
        using std::swap;
        swap(pImpl, other.pImpl);
    }
};
namespace std {
    template<>
    void swap<Widget>(Widget& a, Widget& b) {
        a.swap(b);
    }
}

4.non-member swaps
假设Widget和WidgetImpl都是class templates而非classes

template<typename T>
class WidgetImpl {...};

template<typename T>
class Widget {...};

所以按照上面的例子我们这样做:
namespace std {
    template<>
    void swap<Widget<T> >(Widget& a, Widget& b) { // 错误!不合法!
        a.swap(b);
    }
}

我们企图偏特化一个function template(std::swap), 但是C++只允许对class templates偏特化
然后很自然的我们想到去重载std::swap,像这样:

namespace std {
    template<>
    void swap(Widget<T>& a, Widget<T>& b) {
        a.swap(b);
    }
}

一般而言,重载function templates没有问题,但是std是个特殊的命名空间,上面我们说过,可以全特化std内的templates,但是不要添加新的templates、classes或functions或其他的东西到std里
我们可以这样做:

namespace WidgetStuff {
    template<typename T>
    void swap(Widget<T>& a, Widget<T>& b) { // non-member swap函数,这里并不属于std命名空间
        a.swap(b);
    }
}

以上.


条款26:尽可能延后变量定义式的出现时间

这样做可以增加程序的清晰度并改善程序效率。
1.不只应该延后变量的定义到非得使用它的时候,这还不够,可以延后到这份定义赋初值的时候(避免多余的default构造行为)。
对比下面两个做法就可以理解:

std::string encryptPassword(const std::string password) {
    std::string encrypted; // 1次default构造
    encrypted = password;  // 1次赋值操作
    encrypt(encrypted);
    return encrypted;
}
std::string encryptPassword(const std::string password) {
    std::string encrypted(password);  // 1次copy构造
    encrypt(encrypted);
    return encrypted;
}

2.循环里的构造

Widget w;                       // 1次构造,1次析构,n次赋值
for (int i = 0; i < n; ++i) {
    w = ...;
}
for (int i = 0; i < n; ++i) {   // n次构造,n次析构
    Widget w(...);
}

可以根据具体情况选择哪一种方法。
以上.


条款27:尽量少做转型动作

1.如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts, 如果有个设计需要转型动作,
试着发展无需转型的替代设计。
2.如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需要将转型放进让们自己的代码内。
3.宁可使用C++-style(新式)转型,不要使用旧式转型。这样很容易被辨识出来,且也比较有这分门类别的执掌。

以上.


条款28:避免返回handles指向对象内部成分

注:避免返回handles(包括references、指针、迭代器)指向对象内部。可以增加封装性。
1.帮助const成员函数更像const

class Point {
public:
    Point(int x, int y);

    void setX(int newVal);
    void setY(int newVal);
};

struct RectData {
    Point ulhc; // 左上角upper left-hand corner
    Point lrhc; // 右下角lower right-hand corner
};

class Rectangle {
public:
    Point& upperLeft() const { return pData->ulhc; }
    Point& lowerRight() const { return pData->lrhc; }

private:
    std::tr1::shared_ptr<RectData> pData;
};

Point coord1(0, 0);
Point coord2(100, 100);
const Rectangle rec(coord1, coord2);
rec.upperLeft().setX(50);           // !!!rec应该是const才对

upperLeft调用者能够使用其返回的reference(指向rec内部的Point成员变量)来更改成员,但是原本rec应该是const。
然后我们通常会这样做:

class Rectangle {
public:
    const Point& upperLeft() const { return pData->ulhc; }
    const Point& lowerRight() const { return pData->lrhc; }
}

这样确保了返回的Points是不可被更改的。但是可能在其他场合带来问题。如下:

2.dangling handles(空悬的号码牌):这种handls所指东西(的所属对象)不复存在。

class GUIObject {...};
const Rectangle boundingBox(const GUIObject& obj);

GUIObject* pgo;
const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft()); // !!!

这里boundingBox(*pgo)返回临时变量Rectangle,对这个临时变量upperLeft()函数返回的Point取地址,
然后这个临时变量析构时,pUpperLeft也就变成野指针了。

以上.


条款29:为“异常安全”而努力是值得的

//TODO:


条款30:透彻了解inlining的里里外外

1.将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。
2.不要只因为function templates出现在头文件,就将他们声明为inline。

以上.


条款31:将文件间的编译依存关系降到最低

以上.


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.m或d论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值