如果程序员没有定义,那么编译器会默认隐式为你创建一个copy构造函数,一个copy赋值操作符,一个析构函数。另外如果你没有声明任何构造函数,编译器会为你声明一个default构造函数。

但是只有当这些函数被用到时,他们才会被创建。例如Empty a(b),会创建copy构造函数。

[cpp] view plain copy
  1. class Empty
  2. {
  3. Empty(){...}//default构造函数,如果没有声明任何构造函数,编译器会自动声明
  4. Empty(const Empty& rhs){...}//copy构造函数,如果没有声明,编译器自动声明
  5. ~Empty(){}//析构函数,同copy构造函数
  6. Empty& operator=(const Empty& rhs){...}//copy复制操作符,同上
  7. };

对于copy赋值操作符,有一个需要注意的地方。考虑这样一个类NamedObject,包含一个string& nameValue和一个const int objectValue 。假设你使用编译器为你创建的copy赋值操作符,看看会发生什么:

[cpp] view plain copy
  1. string newDog("Persephone");
  2. string oldDog("Satch");
  3. NamedObject p(newDog,2);
  4. NamedObject s(oldDog,36);
  5. p=s;//将s拷贝赋值到p

p的reference对象nameValue被重新赋值了!我们知道reference对象是不能变动的,因此这总是错误的。同样p的const int objectValue也被改变。

因此如果想在一个内含reference或const成员的类内支持赋值操作,你必须定义自己的copy赋值操作符。

如果你不使用copy构造函数或者copy赋值操作符,你应该将他们声明为private并且不定义它。这样可以防止友元函数或成员函数或派生类使用它。

为多态基类声明virtual析构函数

为防止派生类在销毁时发生错误,即在delete派生类时只有基类部分被销毁,属于派生类的地方却还在,你应该将基类的析构函数声明为virtual。

同时,无端将所有类的析构函数声明为virtual是个错误的习惯,因为virtual会增加对象体积,造成不必要的资源浪费。一个心得是,在类内至少包含一个virtual函数时才为他声明virtual析构函数。

有时候你希望拥有一个抽象类,但是却没有纯虚函数,你可以为它声明一个纯虚析构函数,这样那个类就成为了抽象类。

绝不在构造和析构过程中调用virtual函数

当一个派生类调用构造函数时,若其调用了virtual函数,virtual函数调用的版本为其基类的版本,这不是我们希望看到的。这是因为在派生类对象的基类构造期间,对象的类型是基类而不是派生类。不只virtual函数会被编译器解析至基类,若使用运行期类型信息(dynamic_cast和typeid),也会把对象视为基类。

但是如果你想确保每次一个(继承体系上的)类的对象被创建,就会有适当版本的对象函数(多态函数)被调用,也就是在初始化的过程中调用。你可以将该函数声明为非virtual的,然后每次给他传递必要信息。即如下形式:

[cpp] view plain copy
  1. class Transaction
  2. {
  3. public:
  4. explicit Transaction(constant string& logInfo)
  5. {
  6. logTransaction(logInfo);//调用logTransaction
  7. }
  8. void logTransaction(logInfo) const;//声明为非virtual
  9. };
  10. class BuyTransaction:public Transaction
  11. {
  12. public:
  13. BuyTransaction(parameters):Transaction(parameters){}//每个BuyTransaction都有专属自己的logTransaction信息
  14. };

关于拷贝赋值操作符,有两个编程好习惯

  • 令operator=返回一个reference to *this
  • 在operator=中处理“自我赋值”
[cpp] view plain copy
  1. int x,y,z;
  2. x=y=z=1;

上面的代码能通过编译并且表现得完全符合我们的预期。为了使我们的代码和C++规范保持一致,我们也应该令operator=返回一个对*this的引用。当然你不这样做编译器也不会报错,只是这是一个共同遵守的约定。

某些情况下,会出现一种令我们意想不到的情况:对象自我赋值,如果你够聪明,你当然不会写出这样的代码,但是你不能保证使用你的代码的人也像你一样聪明,所以,这种情况应该被我们考虑到。

看看一个不安全的operator=的实现,看看会发生什么:

[cpp] view plain copy
  1. Widget& Widget::operator=(cosnt Widget& rhs)//widget类中包含了一个Bitmap类的成员pb
  2. {
  3. delete pb;
  4. pb= new Bitmap(*rhs.pb);
  5. return *this;
  6. }

上面的代码一般人当然会这么实现,但是看看当被自我赋值的时候会发生什么。它先删除了自己的pb对象,然后又将pb赋值给它自己,但是我们知道它已经被删除了,这是很明显的一个错误。

一个很好的解决方法是,以下的实现方式:

[cpp] view plain copy
  1. Widget& Widget::operator(cosnt Widget& rhs)
  2. {
  3. Bitmap* pOrig = pb;
  4. pb = new Bitmap(*ths.pb);
  5. delete pOrig;
  6. return *this;
  7. }

拷贝函数应该确保复制对象内的所有成员变量及所有基类成分,因此你应该为派生类撰写拷贝构造函数,因为如果不这样做,派生类的default构造函数会对基类进行default构造,也就是说,派生类的基类部分没有被拷贝,这显然是不符合我们期望的。所以,你应该为派生类撰写拷贝函数。

《Effective C++》读书笔记 被你忽略的关于构造析构赋值的更多相关文章

  1. Effective STL 读书笔记

    Effective STL 读书笔记 标签(空格分隔): 未分类 慎重选择容器类型 标准STL序列容器: vector.string.deque和list(双向列表). 标准STL管理容器: set. ...

  2. Effective STL读书笔记

    Effective STL 读书笔记 本篇文字用于总结在阅读<Effective STL>时的笔记心得,只记录书上描写的,但自己尚未熟练掌握的知识点,不记录通用.常识类的知识点. STL按 ...

  3. 《Effective C++》第2章 构造/析构/赋值运算(2)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  4. 《Effective C++》第2章 构造/析构/赋值运算(1)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  5. Effective C++读书笔记(转)

    第一部分 让自己习惯C++ 条款01:视C++为一个语言联邦 一.要点 ■ c++高效编程守则视状况而变化,取决于你使用c++的哪一部分. 二.扩展 将c++视为一个由相关语言组成的联邦而非单一语言会 ...

  6. effective c++读书笔记(一)

    很早之前就听过这本书,找工作之前读一读.看了几页,个人感觉实在是生涩难懂,非常不符合中国人的思维方式.之前也有博主做过笔记,我来补充一些自己的理解. 我看有人记了笔记,还不错:http://www.3 ...

  7. Effective Java读书笔记完结啦

    Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...

  8. Effective C++ —— 构造/析构/赋值运算(二)

    条款05 : 了解C++默默编写并调用哪些函数 编译器可以暗自为class创建default构造函数.copy构造函数.copy assignment操作符,以及析构函数. 1. default构造函 ...

  9. Effective java读书笔记

    2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...

随机推荐

  1. Google 和 Baidu 常用的搜索技巧

    Google 常用的搜索技巧 1. 精确搜索:双引号 精确搜索,就是在你要搜索的词上,加上双引号,这个Google搜索引擎,就会完全的匹配你所要的词 2. 站内搜索:site 这是一个比较常用的搜索方 ...

  2. 系统优化怎么做-Tomcat优化

    大家好,这里是「聊聊系统优化 」,并在下列地址同步更新 博客园:http://www.cnblogs.com/changsong/ 知乎专栏:https://zhuanlan.zhihu.com/yo ...

  3. DML-删除

    方式一:使用delete一.删除单表的记录★语法:delete from 表名 [where 筛选条件][limit 条目数]二.级联删除[补充]语法:delete 别名1,别名2 from 表1 别 ...

  4. 【2018 ICPC亚洲区域赛南京站 A】Adrien and Austin(博弈)

    题意: 有一排n个石子(注意n可以为0),每次可以取1~K个连续的石子,Adrien先手,Austin后手,若谁不能取则谁输. 思路: (1) n为0时的情况进行特判,后手必胜. (2) 当k=1时, ...

  5. Struts2+Spring+Hibernate整合开发(Maven多模块搭建)

    Struts2+Spring+Hibernate整合开发(Maven多模块搭建) 0.项目结构 Struts2:web层 Spring:对象的容器 Hibernate:数据库持久化操作 1.父模块导入 ...

  6. Java敲地鼠代码

    package test; import java.awt.EventQueue; import java.awt.event.MouseAdapter; import java.awt.event. ...

  7. 月薪30-50K的大数据工程师们,他们背后是如何学习的

    ​ 这两天小编去了解了下大数据开发相关职位的薪资,主要有hadoop工程师,数据挖掘工程师.大数据算法工程师等,从平均薪资来看,目前大数据相关岗位的月薪均在2万以上,随着项目经验的增长工资会越来越高. ...

  8. 前端css之float浮动

    浮动的准则,先找前一个块标签,在确认有否清除浮动的条件或者是距离的情况下,如果这一行能摆得下,就继续紧贴前一个标签 如果摆不下,就会另起一行 浮动只有左边和右边 如果是块标签,设置浮动,先把displ ...

  9. Java学习笔记二十四:Java中的Object类

    Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...

  10. transient是干嘛的

    Java的serialization提供了一种持久化对象实例的机制.当持久化对象时,可能有一个特殊的对象数据成员,我们不想用 serialization机制来保存它.为了在一个特定对象的一个域上关闭s ...