读书笔记 Effective C++: 04 设计与声明

条款18:让接口容易被正确使用,不易被误用

 1. 类型安全性

  枚举enum并不具备类型安全性,可以当作int型使用,可采用类内static函数方式实现。

class Month{

public:

  static Month Jan(){return Month(1);}

  static Month Feb(){return Month(2);}

  ...

  static Month Dec(){return Month(12);}

private:

  explicit Month(int m);

};

使用类内static函数,而不是static变量,主要是non-local static对象的初始化次序有问题(见条款4)。

2. 提供行为一致性的接口

  如:STL每个容器都是用size函数来返回容器中对象的数量,而不是像Java有的用size,有的用length。

3. 阻止误用

  

  把raw point赋值给boost::shared_ptr可以避免资源泄露,自定义deleter函数赋给boost::shared_ptr可以阻止错误的析构机制。

 1 #include <iostream>
 2 #include <boost/shared_ptr.hpp>
 3 
 4 class Test{};
 5 
 6 void deleter(Test* p){
 7     delete p;
 8     p = NULL;
 9     std::cout<<"Test Deleter."<<std::endl;
10 }
11 
12 int main(){
13     boost::shared_ptr<Test> pa(static_cast<Test*>(NULL), deleter);
14     pa.reset(new Test());
15     
16     boost::shared_ptr<Test> pb(new Test(), deleter);
17 }

 

条款19:设计class犹如设计type

1. 新type的对象应该如何被创建和销毁;

2. 对象的初始化和对象的赋值有什么样的差别;

3. 新type的对象如果被passed by value,意味着什么;

4. 新type的合法值,如构造,赋值,setter函数的错误检查;

5. 新type的继承图,各个函数的virtual或者non-virtual;

6. 新type的类型转换;

7. 哪些函数该是member函数;

8. 哪些函数应该声明为private;

9. 新type的函数会被谁调用;

10. 新type的未声明接口;

11. 新type的一般化;

12. 是否真的需要一个新type;

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

1. pass by value至少会带来1次copy构造和1次析构,如果有base class或者成员变量,将会带来更多的构造和析构;

2. pass by value在向上转型的时候,可能会带来切割问题,即只copy构造了base class的部分,而没有构造剩下的那部分;

3. pass by value适合内置类型,STL的迭代器和函数对象;

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

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

  return Rational(lhs.n*rhs.n + lhs.d*rhs.d);

}

正确的返回:直接返回值;

错误1:直接返回stack上的内容,该内容已经销毁;

错误2:返回heap上内容,该内容无法销毁,特别是a*b*c的连续调用,就更难销毁了。

错误3:内部定义static对象,多线程容易出问题,而且单线程也会出问题if(a*b==c*d),这样会导致表达式恒等于true

 

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

1. getter和setter函数提供变量的读写功能。如果变量只读或者只写,就只提供一个相应的函数;

2. 封装,为具体的实现提供充分的弹性; 

 

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

如果一个member函数不访问任何private/protected成员变量或者成员函数,只访问public的成员,那么这个函数就是便利函数(让调用者更方便),这种函数就不应该出现在member函数中。

原因是:1. 较小的集合提供较大的封装性;2. 保证类内的函数都是核心机能函数;

采用的方式是:

namespace WebBrowserStuff{

  class WebBrowser{};

  void clearBrowser(WebBrowser& wb);

}

这是因为,namespace可以跨越多个文件,但是class却不能跨越多个文件。这样就可以把类WebBrowser放在一个.h文件,而便利函数clearBrowser放在另一个.h文件。

如果这样的便利函数很多,就可以把这些便利函数分门别类的放在多个不同的.h文件中,需要哪一类的便利函数,就只需要调用对应的.h文件。

 

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

例如:有理数类Rational的乘法

class Rational{

  public:

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

};

Rational oneHalf(1, 2);

result = oneHalf * 2; //正确

result = 2 * oneHalf; //错误

改成non-member函数:

class Rational{};

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

  return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());

}

posted on 2013-04-25 22:48  LeaGem  阅读(176)  评论(0)    收藏  举报