最近刚看完Effective C++,记录一下当前几个比较经常使用的方法。程序员
智能指针是以对象管理资源,在构造函数中得到资源并在析构函数中释放资源express
如下调用:安全
processWidget(std::tr1::shared_ptr<Widget>(new Widget),priority());ide
建立该调用的代码,编译器要作如下三件事:函数
(1)调用priority布局
(2)执行“new Widget”spa
(3)调用tr1::shared_ptr构造函数设计
C++编译器以什么次序完成这些事呢?弹性很大。能够肯定(2)必定在(3)以前,但(1)能够排在第一或第二或第三执行。若是编译器选择以第二顺位执行它,即(2)(1)(3)的顺序,万一对(1)的调用致使异常,此状况下“new Widget”返回的指针将会遗失,由于它还没有被置入tr1::shared_ptr内,而tr1::shared_ptr是咱们用来防卫资源泄漏的。因此对processWidget的调用过程可能引起资源泄漏。指针
解决办法:使用分离语句,即分别写出(1)建立Widget,(2)将它置入一个智能指针内,而后再把那个智能指针传给processWidget:code
std::tr1::shared_ptr<Widget> pw(new Widget);
ProcessWidget(pw,priority()) ;
以上之因此行得通,是由于编译器对于“跨越语句的各项操做”没有从新排列的自由(自用在语句内它才有那个自由度
考虑如下代码,其中调用validateStudent,后者须要一个Student实参(by value)并返回它是否有效:
bool validateStudent(Student s);
Student plato;
Bool platoIsOK = validateStudent(plato);
上述调用会发生如下:
Student的copy构造函数会被调用,以plato为蓝本将s初始化。validateStudent返回后s会被销毁。所以对此函数而言,参数传递的成本是“一次Student copy构造函数调用,加上一次Student析构函数调用”.
但那还不是所有。Student内有两个string对象,因此每次构造一个Student对象也就构造了两个string对象。此外,Student继承自Person,因此每次构造Student对象也必须构造出一个Person对象。一个Person对象又有两个string对象在其中,所以每一次Person构造动做又需承担两个string构造动做。
最终结果是,以by value方式传递一个Student对象会致使调用一次Studnt copy构造函数、两次Person copy构造函数、四次string copy构造函数。当函数内的Student复件被销毁,每一个构造函数调用动做都须要一个对应的析构函数动做。所以,以by value方式传递一个Student对象,整体成本是“6次构造函数和6次析构函数”!
如今考虑pass by reference-to-const:
bool validateStudent(const Student& s);
这种传递方式的效率高得多:没有任何构造函数或析构函数被调用,由于没有任何新对象被建立。const是很重要的,它保护了传进去的参数不会被修改。
另外,以by reference方式传递参数也能够避免slicing(对象切割)问题。当一个子类对象以by value方式传递并被视为一个基类对象,基类的copy构造函数会被调用,而“形成此对象的行为像个子类对象”的那些特化性质全被切割掉了,仅仅留下一个基类对象。
1.语法一致性
若是成员变量不是public,客户惟一可以访问对象的方法就是经过成员函数。客户对class成员的访问将统一经过成员函数来实现。
2.使用函数可使你对成员变量的处理用更精确的控制。若是你令成员变量为pubilic,每一个人均可读写它,但若是你以函数取得或设定其值,你就能够实现出“不许访问”、“只读访问”、“读写访问”,甚至“只写访问”。
3.封装。若是你经过函数访问变量,往后可改以某个计算替换这个成员变量,而class用户一点也不会知道class的内部实现已经起了变化。
封装的重要性:若是你对客户隐藏成员变量,你能够确保class的约束条件老是会得到维护,由于只有成员函数能够影响他们。并且,你保留了往后变动实现的权利。若是不隐藏他们,即便拥有class源码,改变任何public事物的能力仍是极端受到束缚,由于那会破坏太多客户码。Public意味不封装,而几乎能够说,不封装意味着不可改变,特别是对被普遍采用的class而言。被普遍使用的class是最须要封装的一个族群,由于他们最可以从“改变用一个较佳实现版本”中获益。
假设咱们有一个public成员变量,而咱们最终取消了它。全部使用它的客户码都会被破坏,而那是一个不可知的大量。
protected并不更好。假设咱们有一个protected成员变量,而咱们最终取消了它。全部使用它的子类都会被破坏,而那每每也是是一个不可知的大量。
从封装的角度来讲,其实只有两种访问权限,private和其余。
只要你定义了一个变量而其类型带有一个构造函数或析构函数,那么当程序的控制流到达这个变量定义式时,你便得承受构形成本;当这个变量离开其做用域时,你便得承受析构成本。即便这个变量最终并未被使用,仍需耗费这些成本,因此应当尽量避免这种情形。
更,不仅应该考虑延后变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到可以给它初值实参为止。这样不只可以避免构造(和析构)非必要的对象,还能够避免无心义的default构造行为。
以上两种写法的成本以下:
(1)方法A:1个构造函数+1个析构函数+n个赋值操做
(2)方法B:n个构造函数+n个析构函数
若是class的赋值成本低于一组构造+析构成本,作法A大致而言比较高效。尤为当n值比价大时。不然B比较好。此外方法A形成名称w的做用域比方法B大,有时那对程序的可理解性和易维护性形成冲突。所以,除非(1)你知道赋值成本比“构造+析构”成本低,(2)你正在处理代码中效率高度敏感的部分,不然你应该使用方法B
C风格的转型:
(T)expression //将expression转型为T
函数风格的转型:
T(expression) //将expression转型为T
以上两种称为“旧式转型”,C++的四种新式转型:
(1)const_cast<T>(expression)
一般用来将对象的常量性转除。
(2)dynamic_cast<T>(expression)
主要用来执行“安全向下转型”,也就是用来决定某对象是否属于继承体系中的某个类型。将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来作相应处理。是惟一可能耗费重大运行成本的转型动做。
(3)reinterpret_cast<T>(expression)
T 必须是一个指针、引用、算术类型、函数指针或者成员指针。它能够把一个指针转换成一个整数,也能够把一个整数转换成一个指针
(4)static_cast<T>(expression)
用来强迫隐式转换,例如将non-const对象转为const对象,或将int转为double等等。还能够用来执行上述多种转换的反向转换,例如将void*指针转为typed指针,将point-to-base转为pointer-to-derived。
转型并不是什么都没有作。有的时候须要内部会有一个偏移量来实现。对象的布局方式和它们的地址计算方式随编译器的不一样而不一样,那意味着“因为知道对象布局”而设计的转型,在某一平台行得通,在其余平台并不必定行得通。
dynamic_cast的许多实现版本执行速度至关慢。例如至少有一个很广泛的实现版本基于“class名称之字符串比较”,若是你在四层深的单继承体系内的某个对象身上执行dynamic_cast,这个实现版本每一次dynamic_cast可能会好用多达四次的strcmp调用,用以比较class名称。深度继承或多重继承的成本更高。
【其实这个的内容蛮多,我只把以为重要的两点写了下来】
因此,
(1)若是能够,尽可能避免转型,特别是在注重代码效率的代码中避免dynamic_cast。若是有个设计须要转型动做,试着发展无需转型的替代设计。
(2)若是转型是必要的,试着将他们隐藏于某个函数背后。客户随后能够调用该函数,而不需将转型放进他们本身的代码内。
(3)宁肯使用C++-style转型,不要使用旧式转型。前者很容易辨识出来,并且也比较有着分门别类的执掌。
这段代码能够经过编译。但实际上它是自我矛盾的。一方面upperleft和lowerRight被声明为const成员函数,由于它们的目的只是为了提供客户一个得知Rectangle相关坐标点的方法,而不是让客户修改Rectangle。另外一方面两个函数却都返回reference指向private内部数据,调用者因而经过这些reference更改内部数据。如:
Point coord1(0,0); Point coord2(100,100); const Rectangle rec(coord1,coord2); rec.upperLeft().setX(50);
这里,upperLeft的调用者可以使用被返回的reference来改变成员。但rec其实应该是不可改变的(const)。
上面这种状况是因为“成员函数返回reference”。若是它们返回的是指针或迭代器,相同的状况仍是发生,缘由也相同。reference、指针和迭代器都是所谓的handle,而返回一个“表明对象内部数据”的handle,随之而来的即是“下降对象封装性”的风险。
要解决以上矛盾,只要对它们返回的类型加上const便可:
即便如此,仍是可能在其余场合带来问题。
明确的说,他可能致使空悬的handle:这种handle所指的东西(所属对象)不复存在。
例如某个函数返回GUI对象的外框,
例如某个函数返回GUI对象的外框,如今客户有可能这么使用这个函数:
对boundingBox的调用得到一个新的、暂时的Rectangle对象。这个对象没有名称,权且称他temp。随后upperLeft做用于temp身上,返回一个reference指向temp的内部成分,具体说是指向一个用一标志temp的Point。因而pUpperLeft指向那个Point对象。目前为止一切都还好。
可是,在哪一个语句结束以后,temp将被销毁,而那将直接致使temp内的Point析构。最终将致使pUpperLeft指向一个再也不存在的对象。
这里但愿以D::f从新定义virtual函数B::f,但其中有个错误:B中的f是个const成员,而在D中它未被声明为const。有些编译器可能这样说:
warning:D::f() hides virtual B::f
有些程序员对这个信息的反应是:“噢,固然,D::f遮掩了B::f,那正是想象中该有的事!”错,这个编译器视图告诉你声明与B中名称为f的全部函数并未在D中被从新声明,而是被整个遮掩掉了。
为了让被这样的名称重见天日,可以使用using声明式或转交函数。【这是另一条了】