条款5:了解C++默默编写并调用哪些函数
记住:
★编译器可以(仅仅是可以,并非必须,仅当程序中有这样的用法时才会这么做!!!)暗自为class创建default构造函数,copy构造函数,copy assignment操作符以及析构函数。
--------------------------------------------------------------------------------------------------------------------------------------------
当你写一个空类,编译器会为其声明(仅仅是声明!!!)default构造函数,copy构造函数,copy assignment操作符和析构函数,它们都是public且inline。唯有当这些函数被需要(被调用),它们才会被编译器真正创建出来。
对于copy constructor和copy assignment操作符,编译器创建的版本只是单纯地将来源对象的每个non-static成员变量拷到目标对象。
特殊情形:
对于内含reference或者const成员的class,编译器会拒绝为其生成copy assignment操作符。若你打算这种类支持assignment操作,就必须自己定义copy assignment操作符。还有一种情况:若某个bases classes将copy assignment操作符声明为private,编译器将拒绝为其derived classes生成一个copy assignment操作符。毕竟编译器为derived classes所生的copy assignment操作符想象中可以处理base class成分,但它们当然无法调用derived class无权调用的成员函数。
条款6 若不想使用编译器自动生成的函数,就该明确拒绝
记住:
★为驳回编译器自动(暗自)提供的功能,可将相应的成员函数声明为private并且不予实现。使用像Uncopyable(下面会讲)这样的base class也是一种做法。
--------------------------------------------------------------------------------------
借由声明一个成员函数,可阻止编译器暗自创建其专属版本(注意所有编译器产出的函数都是public);而令这些函数为private,可以阻止人们调用。但即使这样,member functions和friend函数还是可以调用你的private函数,解决方法是,仅声明这个private函数而不去定义,这样在不慎调用时会发生连接错误。(C++ iostream程序库中阻止copying行为就是这么做的!!!)
另一种方法所谓的可将连接期错误移至编译期(好事,毕竟越早侦测出错误越好):
class Uncopyable { //Uncopyable的使用颇为微妙!!! protected:
Uncopyable() {}
~Uncopyable() {} private:
Uncopyable( const Uncopyable & );
Uncopyable& operator=( const Uncopyable& );
}; class HomeForSale : private Uncopyable {
//这样这个class就不用再声明私有的copy构造函数和
//copy assignment操作符
};
这样的话,只要任何人--甚至是member函数或friend函数--尝试拷贝HomeForSale 对象,编译器便试着生成一个copy构造函数和一个copy assignment操作符,而且这些函数的“编译器生成版”会尝试调用其base class的对应兄弟,而那些调用会被编译器拒绝(这就将本在在连接期的错误移到了编译期!!!),∵其base class的拷贝函数是private。不过这样做的一个问题是:使用这项技术可能导致多重继承(∵往往还可能需要继承其他class),而多重继承有时会阻止empty base class optimization(EBO)。
条款7 为多态基类声明virtual析构函数
记住:
★带多态性质的base classes应该声明一个virtual destructor。若class带有任何virtual函数,它就应该拥有一个virtual destructor。
★classes的设计目的若不是作为base classes使用,或不是为了具备多态性,就不该声明virtual destructor。
-----------------------------------------------------------------------------------------------------------------------------------
当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,实际执行时通常发生的是对象的derived成分没被销毁(局部销毁现象)。
当class不企图被当作base class,就无需令其析构函数为virtual。因为这样会增加vptr所占用的空间,解释如下:
欲实现出virtual函数,对象必须携带某些信息,主要用来在运行期决定哪一个virtual函数该被调用。此份信息通常由一个所谓vptr(virtual table pointer)指针指出。vptr指向一个函数指针数组(称vtbl,virtual table);每一个带有virtual函数的class都有一个相应的vtbl。当对象调用某一virtual函数,实际被调用的函数取决于该对象的vptr所指的那个vtbl,编译器会在其中寻找适当的函数指针。
拒绝 继承一个标准容器或任何其他“带有non-virtual析构函数”的class 这样的诱惑!!!
最好为抽象基类声明一个pure virtual destructor并提供空白定义,三个理由:
由于有pure virtual函数,所以构成抽象基类;
由于有个virtual destructor,所以无需担心析构函数的问题;
至于为什么提供空白定义,可以解释如下:
(析构函数的运作方式是,最深层派生的那个class其析构函数最先被调用,然后是其每一个base class的destructor被调用。编译器会在基类的derived classes的destructor中创建一个对基类析构函数的调用动作,所以你必须为这个函数提供一份定义,否则连接器会抱怨)
EC读书笔记系列之3:条款5、条款6、条款7的更多相关文章
-
EC读书笔记系列之12:条款22、23、24
条款22 将成员变量声明为private 记住: ★切记将成员变量声明为private.这可赋予客户访问数据的一致性.可细微划分访问控制.允诺约束条件获得保证,并提供class作者以充分的实现弹性. ...
-
EC读书笔记系列之16:条款35、36、37、38、39、40
条款35 考虑virtual函数以外的其他选择 记住: ★virtual函数的替代方案包括NVI手法及Strategy模式的多种形式.NVI手法自身是一个特殊形式的Template Method模式 ...
-
EC读书笔记系列之1:条款1、条款2、条款3
条款1:视C++为一个语言联邦 记住: ★C++高效编程守则视状况而变化,这取决于你使用C++的哪一部分 C: Object-oriented c++: Template c++: STL 条款2:尽 ...
-
EC读书笔记系列之20:条款53、54、55
条款53 不要轻忽编译器的警告 记住: ★严肃对待编译器发出的警告信息.努力在你的编译器的最高(最严苛)警告级别下争取“无任何警告”的荣誉 ★不要过度依赖编译器的报警能力,∵不同的编译器对待事情的态度 ...
-
EC读书笔记系列之19:条款49、50、51、52
条款49 了解new-handler的行为 记住: ★set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用 ★Nothrow new是一个颇为局限的工具,∵其只适用于内存 ...
-
EC读书笔记系列之18:条款47、48
条款47 请使用traits classes表现类型信息 记住: ★Traits classes使得“类型相关信息”在编译期可用.它们以templates和“templates特化”完成实现 ★整合重 ...
-
EC读书笔记系列之17:条款41、42、43、44、45、46
条款41 了解隐式接口与编译器多态 记住: ★classes和templates都支持接口和多态 ★对classes而言接口是显式的(explicit),以函数签名为中心.多态则是通过virtual函 ...
-
EC读书笔记系列之15:条款32、33、34
条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...
-
EC读书笔记系列之14:条款26、27、28、29、30、31
条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ----------------------- ...
-
EC读书笔记系列之11:条款20、21
条款20 宁以pass-by-reference-to-const替换pass-by-value 记住: ★尽量以pass-by-reference-to-const替换pass-by-value.前 ...
随机推荐
-
fir.im Weekly - iOS 保持界面流畅的技巧
生命不息,coding 不止.本期 fir.im Weekly 收集了微博上的热转资源,包含 Android.iOS 开发工具.源码分享,产品 UI 设计的好文章,还有一些程序员成长的 Tips,希望 ...
-
记一次IIS Express 之经典模式与集成模式切换
Visual Studio 视图 --> 属性窗口. 或者 选中项目 F4 弹出项目的属性窗口进行切换设置..
-
extjs form.load()加载服务端数据
formPanel.getForm().load({ url: 'getApproveRefundInf?refundIdDetail=${refundIdDetail}', waitMsg: '请稍 ...
-
Nginx 配置指令的执行顺序(九)
紧接在 server-rewrite 阶段后边的是 find-config 阶段.这个阶段并不支持 Nginx 模块注册处理程序,而是由 Nginx 核心来完成当前请求与 location 配置块之间 ...
-
QT父子与QT对象delete
原地址:http://www.qteverywhere.com/archives/437 很多C/C++初学者常犯的一个错误就是,使用malloc.new分配了一块内存却忘记释放,导致内存泄漏.Qt的 ...
-
li span兼容性问题
li与span的搭配使用所产的浏览器兼容性问题 1.ls两位,设定了width还是没用.2.总结了一下就是,里面的标签漂浮以后,就不能撑起外层的容器了. 3.li要设至少一个宽度或高度,还要加上ove ...
-
[树莓派]启用root账户
树莓派使用的linux是debian系统,所以树莓派启用root和debian是相同的. debian里root账户默认没有密码,但账户锁定. 当需要root权限时,由默认账户经由sudo执行,Ras ...
-
leetcode 222.Count Complete Tree Nodes
完全二叉树是从左边开始一点点填充节点的,因此需要计算所有的节点的个数. 则分别从左边和右边来进行传递的,当左右是完全二叉树的时候,其节点个数就是pow(2,h)-1. /** * Definition ...
-
React Native: unable to load scripts from assets 'index.android.bundle' on real device
问题:重新建了一个项目后,运行react-native run-android报: unable to load scripts from assets 'index.android.bundle' ...
-
MVC LinqToSql Json DbComparisonExpression 需要具有可比较类型的参数。
需求是:使用post方式传入Controller一个id,然后返回给前台一个Json.突然就报了这个错误,折腾了俩小时, 发现是linq to sql 的语法错了