Effective c++读书笔记

item1:c++一些简单介绍,template, stl之类。

item2:尽量使用const ,enum以及inline。const的使用就不多提了,还是为了保护数据。enum以及inline可以替代define绝大多数的工作。另外需要注意的一点是使用enum可以达到不让通过指针访问这个变量的特点。而inline则主要用于替换define定义的函数

item3:尽可能的使用const,从item2上又展开来讲了下。注意operator中要为const变量设置特定的。

item4:这个item一共提出和解决了两个问题

  • 初始化的问题,初始化最好放在初始化列表里,这样可以提高效率,否则系统还会默认生成一个构造函数
  • 如何解决初始化时需要用到其它类,而其它类不一定初始化的问题。这里采用local static加上函数封装的方式进行处理,然后采取函数调用的方式进行处理。

比如使用 class A;如何保证使用时其已经正确的初始化。这里采用 如下这种凡是可以解决问题2中未初始化的问题。

1  A & Build{
2     static A d;
3     return d;  
4 }

item5:主要说了下c++的一些默认调用,比如如果没有构造函数会默认生成一个。

item6:如何避免一个类被复制时,设置一个基类,将其copy以及operator=的函数声明为private,并且不要实现。当子类需要copy和operator=的default操作时会报错。报错原因是1.子类不能访问private变量 2.只声明未实现

item7:需要将destructor函数设置为virtual函数这样可以避免多态性的问题。如果一个类不准备作为基类则不用设置virual,因为引入virtual会将虚函数表引入,增加内存消耗。

item8: 对于destructor中出现的异常一定要catch,并采用报错或swallow处理。否则会产生大量未处理异常。这会导致很多未确定的问题。

 1 // 中止
 2 A::~A()
 3 {
 4    try(...)
 5    catch(){
 6        std::abort();    
 7    }         
 8 }
 9 
10 //记录
11 A::~A()
12 {
13    try(...)
14    catch(){
15        制作运转记录  
16    }         
17 }

 

item9: 不要在构造以及析构函数中使用virtual函数,因为这样做并不会达到你使用virutal的目的,构造时先构造基类,析构先析构子类,那样virtual并不能指向子类。

item10:对于赋值符号要返回*this,因为要处理其右结合的问题。如果不考虑右结合的问题,那么可以直接copy各个变量。

item11:接上条,赋值要防止赋值给自己。这样会导致在复制前被提前析构。另外复制要注意考虑具体情形,比如指针的赋值通常是要拷贝其指向的数据。

item12:copy函数要确保基类也要被copy,不能仅仅初始化自身的变量。

item13:利用RAII来防止内存泄漏。主要有两个auto_ptr和shared_ptr。(auto_ptr在新的标准里被替换为unique_ptr)

    其中auto_ptr有两个特点 1.离开作用域即自动析构。 2 只能有一个指针指向。shared_ptr想缺少第二个特点(更接**常的使用)。

    注意,这里所说的只能有一个指针指向是因为如果同时有多个指针指向,析构时会析构多次,这是undefined行为

item14:在利用RAII时copy时需要考虑处理方式,并不能完全参照new的方式进行copy,主要的处理方式有以下几种,同时利用RAII出作用域就被释放的功能初始化锁

  • 禁止copy利用设置private uncopy类来实现,参照item6
  • 利用reference count来实现,参照share_ptr
  • 复制底部资源,也就是将其指向的数据也进行复制
  • 转移底部资源拥有权,类似于auto_ptr的处理方式

item15:RAII要提供explici或implicit转换。这是因为利用RAII通常要将其做指针使用,而其实际是个类,所以需要进行转换。如下代码所示

1 int day(const Investment *pi);
2 std::tr1::shared_ptr<Investment>pInv(createInvestment());
3 
4 day(pInv); //oops! pInv is not a pointer
5 day(pInv.get()) //ok

item16:new中带有[],则delete也要带有[],为了防止内存泄漏。

item17:尽量不要将new 放在一个复杂语句中。因为当另外的语句产生异常时,而new已经执行成功,但是没有赋值时。会造成内存泄漏。

Charpter3:这一小章主要讲的是利用auto_ptr以及shared_ptr类防止内存泄漏的问题。auto_ptr以及share_ptr应该也是在底层封装了 new 以及delete操作,导致其可以自动的管理内存,这里要注意其管理类中copy的处理方式,并不能与new 相等,要采取各个的copy策略用以满足需要。

item18:这节主要将要将接口设计好,里边提到将返回的指针设置为shared_ptr从而避免忘记释放内存而导致内存泄漏很有意思。

item19:提出了一些设计类时要考虑的问题

item20:给函数传参数时优先使用const加&组合传递,特别是对于一些类,STL等,单个变量不怎么影响性能。

item21:不要过度优化,当需要返回object的时候不要返回指针。如果强行new一个进行返回指针,虽然造成了性能的提升,但是却引入了内存管理的问题。(即何时释放这个pointer)

item22:将变量设置为private,没啥好说的,为了内聚性。

item23:这节的观点有点意思,如果一个函数可以设置为nomember 时,就不要设置成member,主要还是为了封装的内聚性。

item24: 当operator涉及到两个参数时,要考虑到是否两个参数都是相同的object,如果不是要考虑是不是要放在外边了。比如对于operator*来说,由于*应该满足交换律

1 class A{
2 public:
3     const Rational operator *(const Rational &rhs) const;
4     .....
5 };
6 result =  A * 2; //ok
7 result = 2 * A;//oops!

*item25:Swap的实现。因为swap总是交换两个变量的值,对于某些类来说。并不需要,只需要交换指针就可以了,所以需要对这些类进行重新设计swap,并在swap内调用这些swap。

item26:在你用到某个变量时,尽量晚的定义。因为这里可能之前存在异常导致其无法释放。ps:一些变量如果可以同时在循环内和循环外定义的,尽量在循环内定义。

item27: 讲了下四种标准转换运算符 

    const_cast 将变量的const属性移除,常用在接口转换上。 static_cast 类似与Cstyle的转换,转换要求你知道你指向的对象。

    dynamic_cast 当目标对象不含有强转对象时返回NULL   reinterpret_cast 最危险的一个,pointer都能转成int

    Cstyle cast:因为c设计时并没有object思想,所以当转换类的指针时会有错误

    参考:http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used

                       http://stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast

item28:不要返回一个指向内部成员的handles(references, pointers, iterators),这样破环函数的封装,即使含有const也不能阻止。

*item29: 写函数要保证异常安全,即抛出异常时也能正常处理

item30:在一些小而频繁的程序上使用inline,如果在一些大的程序上使用inline可能导致其稍微修改就要重新编译。而且有些函数(比如默认继承类构造函数)如果使用inline会做占很大的空间(因为会默认调用基类的构造函数)。

*item31:这一节主要讲了如何解决当某个类中包含其它类时,如果成员变量类改变时使本来类不改变的方法。这里利用shared_ptr<..>指针的方法实现这个目的。在类中放置指针指向原有的成员类,这里成员类改变也不会导致包含它的类改变。

item32:利用public集成时要注意集成类是基类的特例,也就是所谓的“is a”的原则。

item33:基类如果有重载函数时继承后继承类如果有相应的函数(但是没有完全的重载函数)可能实现不了重载,这时要利用using语句实现重载,利用转交函数也能实现(forward function)。两者区别在于前种是将所有同名参数的函数都可见,后者则是仅仅特定的参数的函数可见。

item34:pure virtual函数的使用可以避免子类默认调用基类而造成的错误,可以使的子类一定实现某些函数,即使这些函数与基类相同。(Base::)

*item35:

item36:不要把一个重新定义一个实函数,因为这样违反了public继承中 is a 的约束条件。因为is a的要求就是子类一定要继承父类的接口和实现。

item37:默认参数不要用在virtual函数中,因为继承后的类可能仍然会调用基类的默认参数,究其原因是为默认参数是静态绑定,而virtual是动态绑定。

item38:当类中声明其它类时,代表是has a的关系。与public继承区别开来(is a关系)。

item39:这里private要与public继承区分开来,public继承说明在它们概念上有关系。而private继承仅仅是为了实现的方便(为了方便的使用基类的代码)。这里总结下继承实现的三种思想。

  1.is a思想:主要是public继承,说明子类是基类的一个特例。

  2.has a思想:类中包含其它类的情形

  3 is-implemented-in-terms-of:通常基于private继承实现,主要是父类使用基类的相关代码实现。11

item40:慎用多重继承,特别是多重继承中涉及到virtual继承时。virtual继承使得基类只存在一份,在多重继承中可以使用

item41:template与virtual的多态不同在于,template的多态应用在编译时

item42:typename与class在应用在template中时作用基本相同。另外typename可以说明后边的字符串是个类型,而不是成员函数和成员变量。

 1 class MyArray 
 2 { 
 3     public 4     typedef int LengthType;
 5     .....
 6 }
 7 
 8 template<class T>
 9 void MyMethod( T myarr ) 
10 { 
11     typedef typename T::LengthType LengthType; 
12     LengthType length = myarr.GetLength; 
13 }

其中typename的作用就是告诉编译器LengthType是个类型,而不是变量

item43:因为对于template函数而言可能特定声明一个函数与其它不同,所以当template作为基类时需要考虑如何访问基类的函数,一般由三种方法,this, using, Base<T>::。如果不这么声明可能编译时会出错吴,因为对于template而言,是否由错误是运行时决定的。

item44:这里注意指针的转化,把一些需要接受指针的函数的指针利用template设置成通用型的指针

item45:使用template时注意自动转换类型的实现

item46:与item24类似,即使使用template有些operator也要定义为nomember function

item47:

item48:template的一些概念,这里利用template编程的一大好处就是将运行期间可能存在的错误提到编译期间进行检测。

item49:set_new_handler参数里的函数指针,可以在new内存失败时调用

item50:有时要自己实现new以及delete的operator

item51:接上条,new以及delete实现时的一些注意项

item52:new以及delete一定要成对的实现。这样可以避免内存泄漏

item53:注意编译时的警告指示,很多时候错误隐藏在这里

item54:介绍了下stl库以及tr1库

item55:熟悉boost库

 

posted @ 2015-08-04 16:23  qtalker  阅读(278)  评论(0编辑  收藏  举报