effective c++学习总结(条款18--条款29)

条款18:

1.      让接口容易被正确使用,不易被误用。理想上:如果客户企图使用某个接口而却没有获得他所预期的行为(使客户造成误用),这种代码不应该通过编译。

2.      促进正确使用的办法包括接口的一致性,以及与内置类型的行为兼容。

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

1.      在函数之间传递参数时,如果需要传递类对象,尽量使用const引用方式,不要使用传值方式。原因:

(1)    使用传值方式传参,需要调用一次copy构造函数和一次析构函数,如果该对象内部还有类对象的成员变量,还需要对它的成员变量进行构造和析构,效率较低。

(2)    使用const引用,而不直接使用引用,可以避免担心调用者会对参数进行修改。

(3)    通过引用传参按,可以避免出现对象切割的现象。如果使用传值方式传参,当传递一个derived class对象,而参数类型如果是base class,该derived class对象就会被视为base class对象,而通过引用方式不会。

2.      传递内置类型数据,尽量使用传值方式。因为通过引用传参,编译器内部往往通过指针实现,通过指针间接取值比直接取值效率低。

3.      传递STL迭代器和函数对象,比较恰当的方式也是通过传值。因为习惯上它们都被设计成传值方式。当然STL的设计者在实现它们时,也考虑到了高效和不受切割问题的影响。

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

1.      函数返回数据时,绝不要返回指针或引用指向一个local stack对象,因为一旦离开函数作用域,对象就会销毁,返回的数据也变成了不存在的数据。

2.      函数返回数据时,如果返回pointer或reference指向一个local static对象,当同时需要多个这样的返回对象时,可能出现错误。举例:

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

{

   StaticRational  result;

   result = …;

   Return  result;

}

以上函数返回一个local static对象,当调用上述operator*函数时,如果出现下面的情况,就会出现问题:

Rational  a,b,c,d;

……..

If((a*b) == (c*d)){…….}else{…….}

因为a*b和c*d返回同一个对象的引用,不管它们怎么比较,乘积总是相等。

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

1.      切记将成员变量声明为private。恰当的做法:将其声明为private,然后使用接口访问它们。原因:

(1)      从一致性角度,变量声明为private,客户唯一能否访问它们是通过成员函数,否则如果有的变量通过public直接访问,有的通过成员函数访问,用户容易产生迷惑。

(2)      使用函数可以对成员变量的处理有更精确的控制,可以限制它不允许访问、只读、只写、读写都可。

(3)      从封装性角度:将public变量暴露给用户,日后想改变该对象和涉及到的其他对象就受到很多束缚。

2.      Protected变量并不比public更具封装性,因为它将自己暴露给了继承类。从封装的角度,只有两种访问权限:private(提供封装)和其他(不提供封装)

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

1.      宁可拿non-member non-friend函数替换member函数。原因:

(1)      从封装性角度:面向对象守则要求数据尽可能被封装。怎样衡量封装的低和高呢,如果变量有越少的代码、越少的函数可以访问它,数据的封装性就越高。而member函数带来的封装性比non-member函数低。从这一角度,如果变量声明为private或protected,就有无限量的代码可以访问他,封装性就很低,所以不建议声明变量为private或protected。

(2)      增加了包裹弹性和扩充性。

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

如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member。

解释:一个对象调用成员函数时,第一个参数为隐喻参数,为this指针,而该参数不会发生类型转换,只有当参数位于参数列内,这个参数才是隐式类型转换的合格参与者。

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

1.      标准程序库提供了一个swap函数,它是一个模板函数。但当std::swap对你的类型效率不高时,提供一个swap成员函数。解释:Std::swap,需要要swap的变量支持copy构造和copy赋值,如果用户实际要swap的对象包含指针时,高效的方式是指针互换,使用std::swap效率不高。

2.      如果要swap的变量是一个类对象,swap实现方式是:在该类中提供一个member swap,然后实现一个std::swap关于T(T是要swap变量的类型)特化的版本,该函数调用类对象的member swap。

3.      如果要swap的变量是一个类模板,swap实现方式是:在该类中提供一个member swap,然后在该class的命名空间写一个non-member,该函数调用类对象的member swap。

4.      当用户调用swap函数,应针对std::swap使用using声明式,然后调用swap并且不带任何命名空间修饰符:

using std::swap;

Swap(obj1,obj2);该代码的查找顺序为(1):首先在global作用域或T(T为obj1或obj2的变量类型)所在命令空间寻找是否有T专属的swap函数(2):如果没找到,在std中寻找是否关于T特化的函数(3)如果没找到,使用std::swap一般函数

5.      对于类对象,方法3也适用,但最好也提供一个std::swap特化版本,防止用户直接写std::swap(obj1,obj2)时也能调用恰当的函数。

6.      要保证成员版swap不要抛出异常。原因:使用swap可帮助class或类模板提供强烈的异常安全性保障,而该技术基于一个假设:成员版的swap绝不抛出异常。

7.      为“用户定义类型”进行std template全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西。

条款26:尽可能延后变量定义式出现的时间。这样做可增加程序的清晰度变改善程序效率,因为可以避免构造或析构非必要的对象。
条款27:尽量少做转型动作

1.      尽量不要转型,因为转型破坏了类型系统,容易导致麻烦。

2  必须要进行转型时,请使用c++新式转型,尽量不要使用旧式转型。不过可能有一个例外,即调用一个explicit构造函数将一个对象传递给一个函数时:

doSomeWork(Widget(15)); 看起来比doSomeWork(static_cast<Widget>(15));更好。

3.  static_cast与dynamic_cast都可以用于基类和继承类之间转换。区别:

(1)使用dynamic_cast进行类型转换必须存在虚函数,否则编译不通过。

(2)当用于将继承类转换成基类时,两者效果一样。

(3)当将基类转换成继承类时,static_cast不安全,应该使用dynamic_cast。

4.  static_cast与const_cast区别:

(1)static_cast可以在const与非const对象之间进行转换。

(2)static_cast可以将非const指针或引用转换成const 指针或引用,不能将const指针或引用转换成非const指针或引用。

(3)const_cast不能用于对象之间的转换,只能在const指针/引用与非const指针/引用之间转换。

5.dynamic_cast的实现版本一般都很慢,在注重效率的代码中避免使用dynamic_cast。如果有个设计需要转型,试着发展无需转型的替代设计。

6.当在继承类中调用基类的成员函数时,需要直接调用基类的成员函数,不要将*this转型为基类对象再调用它的成员函数。

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

避免返回handle(包括引用、指针、迭代器)指向对象内部。原因:

1.      破坏了封装性。虽然变量声明为private,但由于可通过成员函数访问它们,它们实际上变成了public

2.      如果对象A的const成员函数返回handle,赋值给某个变量,这样就可通过该变量修改A的成员,使该函数不再const。该问题可通过返回const handle来解决。

3.      返回代表对象内部的handle,可能导致dangling handles,即handle指向了已经被销毁的对象。

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

1.      当异常抛出时,带有异常安全性的函数应该保证:

(1)      不泄露任何资源。这可以通过使用对象管理资源的方法解决。

(2)      不允许数据败坏。

2.      异常发生时,会发生什么:

(1)      不再执行throw后面的语句

(2)      在本函数本身寻找匹配的catch块,如果没找到在调用函数的上一层函数寻找,如果一直上朔到main后仍没有找到匹配的catch,系统调用terminate终止程序。如果找到了匹配的catch语句,则执行完catch后,执行后面的语句。

(3)      在执行catch语句之前,系统会自动销毁过早终止函数的局部stack变量,但不会销毁动态分配的对象。如果在调对象的析构函数销毁对象时,有抛出异常,系统会调terminate终止程序,因此要保证析构函数不要抛出异常。

3.      异常安全函数必须提供以下三个保证之一:

(1)      基本承若:如果异常抛出,程序内的所有对象都处于一种内部前后一致的状态。但保证程序处于一种合法状态,但状态不定。

(2)      强烈保证:如果异常抛出,程序状态不变。即:要么调用成功,要么回复到调用函数之前的状态。

(3)      不抛出异常。通常很难保证函数不调不抛异常的函数,所以该保证很难保证。

4.      “强烈保证”往往能否以copy-and-swap实现出来:为打算修改的对象做一份副本,然后在副本身上做必要修改,若抛出异常,原对象保持不变。待改变都成功后,才将副本和原对象交换。但“强烈保证”并非对所有函数都可实现或具备现实意义。

5.      函数提供的“异常安全保证“通常最高只等于其所调用各个函数的”异常安全保证“中的最弱者。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值