首页IT科技c++太麻烦(Effective C++学习笔记(2))

c++太麻烦(Effective C++学习笔记(2))

时间2025-09-09 19:34:11分类IT科技浏览6079
导读:条款5:了解C++默默编写并调用那些函数 如果类中没有声明构造函数,编译器会帮你生出一个default构造函数、copy构造函数、copy assignment操作符以及析构函数...

条款5:了解C++默默编写并调用那些函数

如果类中没有声明构造函数                ,编译器会帮你生出一个default构造函数                、copy构造函数                        、copy assignment操作符以及析构函数                。其中                        ,default构造函数和析构函数会分别自动调用基类(父类)成员变量的构造函数和析构函数        ,而copy构造函数和copy assignment操作符只是简单地将每一个非static成员变量拷贝到目标对象(浅拷贝

)                        。 有两种情况下编译器拒绝自动为class生成operator=:

(1)类内含引用&成员变量和const成员变量        ,这两者一经初始化就不允许再赋值                        ,但可自定义copy操作符支持赋值;

(2)如果某个基类将copy assignment操作符设为private                ,以至于派生类无法访问        。

条款6:如果不想使用编译器自动生成的函数        ,就应该明确拒绝

如果想要阻止编译器暗自创建copy构造函数        、copy assignment操作符                        ,同时阻止其他人调用它们                ,有以下两种实现方法:

(1)将这两个成员函数声明为private并且故意不实现它们,形参参数名也可以不写                        ,因为根本不会实现它们:

(2)(将链接器错误转移至编译器)继承一个包含private声明的copy构造函数                、copy assignment操作符但不实现的基类                        ,然后继承它        。因为编译器生成的copy会尝试调用基类对应的函数,由于private                ,则会被拒绝                        。

条款7:为多态基类声明virtual析构函数

当derived class对象经由一个base class(带着非virtual析构函数)指针删除时                        ,其结果未定义—通常是derived class自己的成员没有被删除        ,造成对象“局部销毁                ”                。

(1)解决方法:给基类一个虚析构函数 (2)底层原理:本质上是多态的体现        。基类一个虚析构函数                ,其派生类的析构函数将继承虚函性质                        ,也是一个虚函数                        。当一个类有虚函数时        ,实例化对象中会有一个虚函数表指针vptr        ,指向该对象的虚函数表                。虚函数表中存放了该对象的所有虚函数。虚析构函数也会放到虚函数表中                        。当基类指针指向派生类对象时                        ,虚函数表中的析构函数是派生类自己的析构函数                        。因此                ,当delete此基类指针时        ,vptr在虚函数表中查询并执行的析构函数是派生类自定义的析构函数                        ,而非基类的析构函数。与此同时                ,派生类析构函数被调用后,基类析构函数自动调用                。也就是说                        ,派生类对象拥有的基类和自己的成员均被析构                        。 带有多态性质基类应该声明一个虚析构函数        。如果类带有任何虚析构函数                        ,它就应该拥有一个虚析构函数                。 不要把标准库的类当作基类,可能它们不含任何虚函数                        。因此有可能发生上述“局部销毁                        ”的情况        。 另一种解决对象“局部销毁        ”做法是为抽象类添加纯虚析构函数                ,让其他类继承它        。需要为其声明                        ,并且定义(因为派生类执行析构函数时要调用基类析构函数)                        。 virtual ~ABC() = 0;//声明 virtual ABC::~ABC(){};//定义

条款8:别让异常逃离析构函数

当存在多个对象同时销毁时        ,可能存在多个对象在析构部分抛出异常                。程序将结束执行或者导致不明确行为        。 析构函数中发生异常的两种处理方式:

(1)结束程序

:可以阻止异常从析构函数传播出去(下例中为析构函数中调用的close()可能出现异常)

(2)吞下异常

:有时候希望程序即使遇到异常以后也要让程序继续执行一段时间(例如数据保存工作等等)                        。

析构函数绝对不要吐出异常                。如果一个被析构函数调用的函数可能抛出异常                ,析构函数应该捕捉任何异常                        ,然后吞下它们(不传播)或结束程序

。 如果客户需要对某个操作函数运行期间抛出的异常做出反应        ,那么class应该提供一个普通函数(而且在析构函数中)执行该操作                        。

条款9:绝不在构造和析构过程中调用virtual函数

构造和析构函数中使用虚函数可能不会产生多态行为        ,与预期不同                        。base class构造期间virtual函数绝不会下降到derived class阶层

。

原因:在派生类对象的基类构造期间                        ,对象的类型是基类而不是派生类                ,此时执行的虚函数是基类的虚函数而不是派生类的虚函数                。对象在派生类构造函数开始执行之前不会成为一个派生类对象                        。析构函数同理        。 有一种情况下很难侦测到:基类构造函数没有直接调用虚函数        ,而是调用了将虚函数封装到一个非virtual的函数中                        ,此时依然会发生构造基类部分调用基类的虚函数的情况                ,但是难以检测                。因此,在构造和析构期间不要调用虚函数                        ,包括它们所调用的函数也不要存在调用虚函数                        ,因为这类调用从不下降至派生类                        。 如果需要不同派生类在构造时,实现相对应的功能        。解决办法就是让基类实现对应功能的函数为非virtual                ,派生类在构造中传递实现对应功能的信息给基类构造函数                        ,基类构造函数将信息给事项对应功能的非virtual函数        。通常这些携带传递信息的变量或者函数被设为static        ,保证使用的变量已经初始化

                        。由于派生类成员构造在基类成员构造之后                ,因此如果不用static成员传递信息                        ,派生类其他成员可能是未初始化的                。

条款10:令operate=返回一个reference to *this

为了实现“连锁赋值        ”        ,赋值操作符必须返回一个引用指向操作符左侧实参        。此条款也适用于其他赋值相关运算        ,例如+=                        ,-=                ,*=等等                        。 X = Y = C = 15;//连锁赋值

条款11:在operate=中处理“自我赋值                        ”

自我赋值类似如下:

自我赋值可能会产生不安全的情况                。如下例所示        ,如果是同一对象                        ,delete销毁是this->pb所指向的区域                ,同时是rhs.pb所指向的区域,那么自我赋值后返回的对象中的pb指针指向的是一个已删除的区域。

解决方法:

(1)正同测试:

(2)创建副本或者以by value方式接收传入的实参(也是一份副本)                        ,这样赋值操作的两个对象/成员变量本质上是两个数据内容可能一样但实际是两个单独的对象/成员变量                        ,copy and swap方式:

确保当对象自我赋值时operator有良好的行为                        。其中技术包括比较“来源对象                ”和“目标对象        ”的地址                        、精心周到的语句顺序,以及copy and swap                        。确定任何函数如果操作一个以上的对象                ,而其中多个对象是同一个对象时                        ,其行为仍然正确。

条款12:复制对象时勿忘其每一个成分

当为class添加一个成员变量        ,你必须同时修改copying函数(所有的拷贝构造以及operator=函数)                。 如果要为derived class 撰写copying函数                ,必须小心复制其base class成分                        。如果是private成员                        ,应该让derived class 的copying函数调用相应的base class函数        。确保**(1)复制所有local成员变量;(2)调用所有基类内的适当的copying函数**                。

不要尝试以某一个copying函数实现另一个copying函数                        。应该将共同重复机能放进第三个函数中        ,并由两个copying函数共同调用        。
声明:本站所有文章        ,如无特殊说明或标注                        ,均为本站原创发布        。任何个人或组织                ,在未征得本站同意时        ,禁止复制        、盗用        、采集                        、发布本站内容到任何网站                、书籍等各类媒体平台                        。如若本站内容侵犯了原著者的合法权益                        ,可联系我们进行处理                。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
搜狗输入法自动搜索(搜狗url自动推送工具-搜狗批量提交软件)