年前忙了几天,到现在才算是有空休息下来。先祝大家新年快乐,心想事成:)我也会发笑脸o.o

  这篇博文主要介绍定义一个类型的对象时的复制控制方式,这部分内容之前有一定的了解但又浅尝辄止,始终感觉没能找到要点。年前又拿起书细细品读,算是有了一点新的了解。几天前就想动笔了,一直没时间,拖到现在。

  每种类型定义了创建该类型的对象时会发生什么——构造函数定义了该类类型对象的初始化。类型还能控制复制、赋值或撤销该类型的对象时会发生什么——类通过特殊的成员函数:复制构造函数、赋值操作符和析构函数来控制这些行为。

  复制构造函数是一种特殊构造函数,具有单个形参,该形参(常用 const 修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。当将该类型的对象传递给函数或函数返回该类型的对象时,将隐式使用复制构造函数。赋值操作符可以通过指定不同类型的右操作数而重载。右操作数为类类型的版本比较特殊:如果我们没有编写这种版本,编译器将为我们合成一个。析构函数是构造函数的互补:当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。析构函数可用于释放对象时构造或在对象的生命期中所获取的资源。不管类是否定义了自己的析构函数,编译器都自动执行类中非static 数据成员的析构函数。复制构造函数、赋值操作符和析构函数总称为复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。

复制构造函数

  • 只有单个形参,而且该形参是对本类类型对象的引用(常用 const 修饰),这样的构造函数称为复制构造函数。与默认构造函数一样,复制构造函数可由编译器隐式调用。
  • 如果我们没有定义复制构造函数,编译器就会为我们合成一个。与合成的默认构造函数不同,即使我们定义了其他构造函数,也会合成复制构造函数。合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。
  • 通常,定义复制构造函数最困难的部分在于认识到需要复制构造函数。只要能认识到需要复制构造函数,定义构造函数一般非常简单。复制构造函数的定义与其他构造函数一样:它与类同名,没有返回值,可以(而且应该)使用构造函数初始化列表初始化新创建对象的成员,可以在函数体中做任何其他必要工作。
  • 有些类需要完全禁止复制。例如,iostream 类就不允许复制。如果想要禁止复制,似乎可以省略复制构造函数,然而,如果不定义复制构造函数,编译器将合成一个。为了防止复制,类必须显式声明其复制构造函数为 private。然而,类的友元和成员仍可以进行复制。如果想要连友元和成员中的复制也禁止,就可以声明一个(private)复制构造函数但不对其定义。声明而不定义成员函数是合法的,但是,使用未定义成员的任何尝试将导致链接失败。通过声明(但不定义)private 复制构造函数,可以禁止任何复制类类型对象的尝试:用户代码中复制尝试将在编译时标记为错误,而成员函数和友元中的复制尝试将在链接时导致错误。

赋值操作符

  • 重载操作符是一些函数,其名字为 operator 后跟着所定义的操作符的符号。因此,通过定义名为 operator= 的函数,我们可以对赋值进行定义。像任何其他函数一样,操作符函数有一个返回值和一个形参表。形参表必须具有与该操作符数目相同的形参(如果操作符是一个类成员,则包括隐式 this 形参。赋值是二元运算,所以该操作符函数有两个形参:第一个形参对应着左操作数,第二个形参对应右操作数。大多数操作符可以定义为成员函数或非成员函数。当操作符为成员函数时,它的第一个操作数隐式绑定到 this 指针。有些操作符(包括赋值操作符)必须是定义自己的类的成员。因为赋值必须是类的成员,所以 this 绑定到指向左操作数的指针。因此,赋值操作符接受单个形参,且该形参是同一类类型的对象。右操作数一般作为 const 引用传递。赋值操作符的返回类型应该与内置类型赋值运算返回的类型相同。内置类型的赋值运算返回对右操作数的引用,因此,赋值操作符也返回对同一类类型的引用。

析构函数

  • 构造函数的一个用途是自动获取资源。例如,构造函数可以分配一个缓冲区或打开一个文件,在构造函数中分配了资源之后,需要一个对应操作自动回收或释放资源。析构函数就是这样的一个特殊函数,它可以完成所需的资源回收,作为类构造函数的补充。
  • 许多类不需要显式析构函数,尤其是具有构造函数的类不一定需要定义自己的析构函数。仅在有些工作需要析构函数完成时,才需要析构函数。析构函数通常用于释放在构造函数或在对象生命期内获取的资源。如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。这个规则常称为三法则,指的是如果需要析构函数,则需要所有这三个复制控制成员。与复制构造函数或赋值操作符不同,编译器总是会为我们合成一个析构函数。合成析构函数按对象创建时的逆序撤销每个非 static 成员,因此,它按成员在类中声明次序的逆序撤销成员。对于类类型的每个成员,合成析构函数调用该成员的析构函数来撤销对象。
  • 析构函数是个成员函数,它的名字是在类名字之前加上一个代字号(~),它没有返回值,没有形参。因为不能指定任何形参,所以不能重载析构函数。虽然可以为一个类定义多个构造函数,但只能提供一个析构函数,应用于类的所有对象。析构函数与复制构造函数或赋值操作符之间的一个重要区别是,即使我们编写了自己的析构函数,合成析构函数仍然运行。

管理指针成员

  通过不同的复制控制策略,可以为指针成员实现不同的行为。其中一种便是智能指针。关于这部分内容,我打算在下一篇专门讲指针的博客中详加阐述(如果有细心的读者看过本系列博客前文,应该注意到我到现在都没有提及指针这一不可避免的特性)。本文内容未完,还有一个消息处理实例没有展示,有时间再更新,现在有点累哈哈。

  最后再祝大家新年快乐!

C++拾遗(六)——复制控制的更多相关文章

  1. C++ 复制控制之复制构造函数

    7月26日更新: 过了这么长的时间回过头来看,发现文章中有几个点说错(用红字标出): 构造函数不是只有唯一一个参数,它也可以是多参数形式,其第二参数及后继以一个默认值供应. 不是没有声明复制控制函数时 ...

  2. C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承

    面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针 ...

  3. C++ Primer 随笔 Chapter 13 复制控制

    1.复制控制包含的内容:复制构造函数.赋值操作符.析构函数 2.复制构造函数: a. 定义:只有单个形参,而且该形参是对本类类型的引用,这样的构造函数被成为复制构造函数 b. 适用情况: (1)根据一 ...

  4. C++Primer笔记之复制控制

    复制控制这一节需要注意的地方不多,主要有以下几点: 1.定义自己的复制构造函数 什么时候需要定义自己的复制构造函数,而不用系统提供的,主要遵循以下的经验说明: 某些类必须对复制对象时发生的事情加以控制 ...

  5. 稍微深入点理解C++复制控制【转】

    通过一个实例稍微深入理解C++复制控制过程,参考资料<C++ primer>,介绍点基本知识: 1.在C++中类通过特殊的成员函数:复制构造函数.赋值操作符和析构函数来控制复制.赋值和撤销 ...

  6. C++继承与构造函数、复制控制

    每个派生类对象由派生类中定义的(非static)成员加上一个或多个基类子对象构成,因此,当构造.复制.赋值和撤销派生类型对象时,也会构造.复制.赋值和撤销这些基类子对象. 构造函数和复制控制成员不能继 ...

  7. 《Linux命令行与shell脚本编程大全》第十六章 控制脚本

    一些控制脚本的方式:向脚本发送信号.修改脚本优先级,在脚本运行时切换到运行模式 16.1 处理信号 linux利用信号与运行在系统中的进程进行通信. 也可以通过对脚本进行编程,使其在收到特定信号时执行 ...

  8. C++复制控制

    1.复制构造函数可用于: (1)根据另一个同类型的对象显示或隐式初始化一个对象 string str1="test";   //隐式 string str2=str1; //显示 ...

  9. WPF六个控制概述

    在线演示:http://v.youku.com/v_show/id_XNzA0NjU1Mjk2.html 清晰版视频+代码下载:http://115.com/lb/5lbcftnrfo9s 一.简单介 ...

随机推荐

  1. AAAAAA

    有可能被立案调查.暂停上市.退市风险警示*ST.特别处理ST的公司:银鸽投资(SH:600069).天山生物(SZ:300313).金贵银业(SZ:002716).美盛文化(SZ:002699).未名 ...

  2. Maven查找添加方式

    可以通过以下链接 : https://mvnrepository.com/

  3. Oracle数据库恢复之resetlogs

    实验环境:RHEL 5.4 + Oracle 11.2.0.3 如果是一名合格的Oracle DBA,对resetlogs这种关键字都应该是极其敏感的,当确认需要这种操作时一定要三思而后行,如果自己不 ...

  4. 【转】mysql日期格式+日期增加天数

    WHERE DATE_FORMAT(字段,'%m/%d')=DATE_FORMAT(DATE_ADD(SYSDATE(),INTERVAL 7 DAY),'%m/%d') 条件 数据库字段时间=当前时 ...

  5. luogu P5358 [SDOI2019]快速查询【模拟(?)】

    把有单点修改和查询的点离散进一个数组,然后单点修改直接改,记录一个修改时间t,维护一个sm表示这些离散的点的和,val表示出了离散点其他点的值,因为都是一样的所以只记录这一个值即可,记录ljlc为加法 ...

  6. tarjan求割点割边的思考

    这个文章的思路是按照这里来的.这里讨论的都是无向图.应该有向图也差不多. 1.如何求割点 首先来看求割点.割点必须满足去掉其以后,图被分割.tarjan算法考虑了两个: 根节点如果有两颗及以上子树,它 ...

  7. openinstall的免费服务对App推广有哪些帮助?

    想在微信中下载 App,需要“点击右上角打开浏览器”,太麻烦: 想深入查看微信分享内容,需要手动打开 App 搜索,不方便: 想给 App 做推广,Android 需要多个渠道人工打包,效率低: .. ...

  8. Mac下通过homebrew安装maven

    1.安装Homebrew 将以下命令粘贴至终端 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebr ...

  9. git push error: ! [rejected] failed to push some refs to . . .

    报错情况: 报错原因:远程库与本地库不一致造成的,需要把远程库同步到本地库! 解决办法: git pull --rebase origin master 这条指令是将远程库中的更新合并到本地库,--r ...

  10. Django-Rest-Framework的视图和路由

    Django-Rest-Framework的视图和路由  restful framework  Django-Rest-Framework的视图 APIView django中写CBV的时候继承的是V ...