如果程序员没有定义,那么编译器会默认隐式为你创建一个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. 锐捷交换机实验案例:vlan间互访的配置与验证

    组网需求: 1.如下图所示,某用户内网被划分为VLAN 10.VLAN 20.VLAN 30,以实现相互间的2 层隔离: 2.3 个VLAN 对应的IP 子网分别为192.168.10.0/24 .1 ...

  2. Oracle在线重定义(online redefinition)--将普通表改为分区表

    使用Oracle的在线重定义技术,可以将Oracle的普通表改为分区表.操作如下: STEP1:测试表是否可以在线重定义,这里以unixdev数据库的LIJIAMAN.BSTEST为例 EXEC DB ...

  3. Oracle中插入千万条测试数据

    测试需求,id.姓名.邮箱.手机号不可重复 1.创建序列 create sequence id_sequence; //创建序列id_sequence 2.创建表 create table USERI ...

  4. Oracle安装时出错,TNS-04612 :"XXX"的RHS 为空

    安装Oracle11g时到最后一步,一直出现 “Oracle Net Configuration Assistant 失败” 试了好多次一直都没成功,在日志文件中的找到这几个错误: TNS-04404 ...

  5. 字符编码ascii、unicode、utf-­‐8、gbk 的关系

    ASIIC码: 计算机是美国人发明和最早使用的,他们为了解决计算机处理字符串的问题,就将数字字母和一些常用的符号做成了一套编码,这个编码就是ASIIC码.ASIIC码包括数字大小写字母和常用符号,一共 ...

  6. restframework中的那些参数你知道吗?

    序列化是很重要的过程, 在构建数据结构的时候, 往往会出现很多意想不到的问题, 有一些参数你要用, 但是没有办法穿过来, 怎么办> 今天这篇博客就是写我之前的一个小项目中用restframewo ...

  7. django_ORM学生管理系统

    一.新建django项目准备工作 CMD新建项目命令:django-admin startproject [项目名称] pycharm的project目录里新建app命令:python manage. ...

  8. Go编写一个比特币交易自动出价程序

    语言环境为>=go1.10 go语言环境不多说 实现目的能与BitMEX api进行交互自动交易,目前虚拟币平台很多,平台API实现也很容易.后续会加上其它平台和自动交易算法策略,具体看平台交易 ...

  9. openwrt从0开始-目录

    终于下定决心把近期的笔记整理一下.涉及到方方面面,记录自己的成长和沉淀自己所学. 预备知识:linux, 网络通信,待补充... 目录: 前言:openwrt简介 1. openwrt源码下载及编译环 ...

  10. 论文翻译第二弹--用python(或Markdown)对论文复制文本进行处理

    图中这种论文你想进行文本复制放入翻译软件进行翻译时,会发现是这种形式: 句子之间是断开的,这时普遍的方法,也是我之前一直用的方法就是打开一个文档编辑器,复制上去后一行行地继续调整.昨天不想这样了,就打 ...