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

通过一个实例稍微深入理解C++复制控制过程,参考资料《C++ primer》,介绍点基本知识:

1、在C++中类通过特殊的成员函数:复制构造函数、赋值操作符和析构函数来控制复制、赋值和撤销该类的对象时会发生什么。

2、复制构造函数(copy constructor)是一种特殊的构造函数,具有单个形参,该形参(常用const)是对该类类型的引用;

  • 当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用复制构造函数;
  • 当将该类的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造函数。

3、复制构造函数可用于:

  • 根据另一个同类型的对象显示或隐式初始化一个对象;
  • 复制一个对象,将它作为实参传给一个函数;
  • 从函数返回时复制一个对象;
  • 初始化顺序容器中的元素;
  • 根据元素初始化式列表初始化数组元素。

4、如果我们在类体中没有定义复制构造函数,编译器就会为我们合成一个复制构造函数。与合成的默认构造函数不同,即使我们定义了其他构造函数,也会合成复制构造函数。

合成复制构造函数的行为:执行逐个成员初始化,将新对象初始化为原对象的副本。

 有些类经常有的数据成员是指针,这时我们必须对复制对象时发生的事情加以控制,否则当复制的对象析构或者删除时,对象中的指针不再指向有效对象,则会出现悬垂指针情况,这时我们必须要定义自己的复制构造函数。

5、有些类需要完全禁止复制,例如,iostream类就不允许复制,如果不想复制,则应明确禁止,可以将复制构造函数声明为private,并且不定义。

6、赋值操作符(assignment operator):同复制构造函数一样,如果在类体中没有定义赋值操作符,编译器也会为我们合成一个赋值操作符。它会执行逐个成员复制:右操作数对象的每个成员赋值给左操作数对象的对应成员。

    如:

Sales_item& Sales_item::operator=(const Sales_item &rhs)
{
    isbn = rhs.isbn;
    units_sold = rhs.units_sold;
    revenue = rhs.revenue;
    return *this;
}

7、析构函数(destructor):是构造函数的互补,当对象超出作用域或动态分配的对象(new、malloc)被删除时,将自动应用析构函数。

下面通过一个实例详细说明复制构造函数、赋值操作符、析构函数的调用机制,代码中已有详细解释,故在此不再赘述~

 1 /************************************************
 2 *
 3 *     内容摘要:定义Expm1类,该类给出复制控制成员和其他构造函数
 4 *              用不同方式使用Expm1类型的对象:
 5 *              作为非引用形参和引用形参传递,动态分配
 6 *              作为函数返回值,进行赋值操作,作为元素放在vector容器中。
 7 *              研究何时执行哪个构造函数和复制控制成员
 8 *     9 *     日    期:2012.8.27 by Jacky Liu
10 *
11 ************************************************/
12
13 #include <vector>
14 #include <iostream>
15
16 struct Expm1
17 {
18     //默认构造函数
19     Expm1()
20     {
21         std::cout<<"Expm1()"<<std::endl;
22     }
23
24     //复制构造函数
25     Expm1(const Expm1 &)
26     {
27         std::cout<<"Expm1 (const Expm1&)"<<std::endl;
28     }
29
30     //复制操作符
31     Expm1 & operator = (const Expm1 &)
32     {
33         std::cout<<"operator = (const Expm1&)"<<std::endl;
34         return *this;
35     }
36
37     //析构函数
38     ~Expm1()
39     {
40         std::cout<<"~Expm1()"<<std::endl;
41     }
42 };
43
44 void func1(Expm1 obj)        //形参为Expm1对象
45 {}
46
47 void func2(Expm1 & obj)      //形参为Expm1对象的引用
48 {}
49
50 Expm1 func3()
51 {
52     Expm1 obj;
53     return obj;  //返回Expm1对象
54 }
55
56 int main()
57 {
58     std::cout<<"1-------------------------------------"<<std::endl<<std::endl;
59
60     Expm1 eobj;     //调用默认构造函数创建Expm1对象eobj
61
62     std::cout<<"2-------------------------------------"<<std::endl<<std::endl;
63
64     func1(eobj);    //调用复制构造函数
65                     //将形参Expm1对象创建为实参Expm1对象的副本
66                     //函数执行完毕后调用析构函数撤销形参Expm1对象
67
68     std::cout<<"3-------------------------------------"<<std::endl<<std::endl;
69
70     func2(eobj);   //形参为Expm1对象的引用,无需调用复制构造函数传递实参
71
72     std::cout<<"4-------------------------------------"<<std::endl<<std::endl;
73
74     eobj = func3();   //调用默认构造函数创建局部Expm1对象
75                       //函数返回时调用复制构造函数创建作为返回值副本的Expm1对象
76                       //然后调用析构函数撤销局部Expm1对象
77                       //然后调用赋值操作符
78                       //执行完赋值操作后
79                       //调用析构函数撤销作为返回值副本的Expm1对象
80
81     std::cout<<"5-------------------------------------"<<std::endl<<std::endl;
82
83     Expm1 *p = new Expm1;  //调用默认构造函数动态创建Expm1对象
84
85     std::vector<Expm1> evec(3);    //调用默认构造函数创建一个临时值Expm1对象
86                                    //调用复制构造函数,将临时值Expm1对象复制到vector容器evec的每个元素
87                                    //调用析构函数撤销临时值Expm1对象
88                                    //按以上重复三次
89
90     std::cout<<"6-------------------------------------"<<std::endl<<std::endl;
91     delete p;        //调用析构函数撤销动态创建的Expm1对象
92
93     system("pause");
94     return 0;        //eobj及evec生命期结束,自动调用析构函数撤销
95
96 }

运行结果:仔细观察运行结果的执行情况,可以深入理解C++中的复制控制机制~

时间: 2024-12-21 05:35:07

稍微深入点理解C++复制控制【转】的相关文章

c++OOP之复制控制 ------复制构造函数、赋值重载、析构

本博文我们讨论OOP复制控制的一些内容: 首先考虑对象复制的时机: 非引用类型 1):根据一个类去显式或者隐式初始化一个对象: 2):复制一个对象,将它作为实参传给一个函数: 3):从函数返回时复制一个对象.(string tolittle(string word)) 一个空类,编译器提供默认无参数构造函数.拷贝构造函数.赋值运算符以及析构函数,一共四个函数.(面试) 11.复制构造函数.赋值运算符以及析构函数,称为三法则,一旦提供了其中一个,务必提供其余两个.以String为例: a) 涉及到

C++ Primer 学习笔记_68_面向对象编程 --构造函数和复制控制[续]

面向对象编程 --构造函数和复制控制[续] 三.复制控制和继承 合成操作对对象的基类部分连同派生类部分的成员一起进行复制.赋值或撤销,使用基类的复制构造函数.赋值操作符或析构函数对基类部分进行复制.赋值或撤销. 类是否需要定义复制控制成员完全取决于类自身的直接成员.基类可以定义自己的复制控制而派生类使用合成版本,反之,基类使用合成版本,而派生类使用自己定义的复制控制也可以. 只包含类类型或内置类型的数据成员.不包含指针的类一般可以使用合成操作,复制.赋值或撤销这样的成员不需要使用特殊控制.但是:

C++独孤九剑第五式——人生几何(对象复制控制)

对酒当歌,人生几何? 譬如朝露,去日苦多. 人的一生可能惊涛骇浪,更可能波澜不惊,这次我们就来探讨一下"对象"(当然各位同学自己的对象不在本次讨论范围之内O(∩_∩)O,课后自己讨论吧)一生的"起起落落",即对象的复制控制. 复制控制包括三个部分:复制构造函数的调用.赋值操作符的调用.析构函数的调用.下面就这三个操作来逐一进行介绍,大家共同学习(*^-^*) 一.复制构造函数 复制构造函数:首先它也是构造函数,所以函数名与类名相同,没有返回值:其次,它只有一个形参,

C++ Primer 学习笔记_68_面向对象编程 -构造函数跟复制控制[续]

面向对象编程 --构造函数和复制控制[续] 三.复制控制和继承 合成操作对对象的基类部分连同派生类部分的成员一起进行复制.赋值或撤销,使用基类的复制构造函数.赋值操作符或析构函数对基类部分进行复制.赋值或撤销. 类是否需要定义复制控制成员完全取决于类自身的直接成员.基类可以定义自己的复制控制而派生类使用合成版本,反之,基类使用合成版本,而派生类使用自己定义的复制控制也可以. 只包含类类型或内置类型的数据成员.不包含指针的类一般可以使用合成操作,复制.赋值或撤销这样的成员不需要使用特殊控制.但是:

第十九篇:复制控制( 下 ) --- 自定义析构函数

前言 经过前两篇随笔( 上  中 )的分析我们已经解决了具有指针成员的同类对象“ 干涉 ”问题.可惜,前面给出的解决方案代码还是不完整.还有什么问题呢?观察发现,构造函数里面有new的关键字出现,也就是说开辟了新的内存空间,我们也知道new必须也只能对应一个delete,而不应该让系统自己处理( 还不熟练new和delete用法的点这里 ),但这里和new对应的delete去哪里了? 解决思路 应该何时delete?显然,应该在对象销毁的时候delete.C++中定义了这样一种函数 --- 析构

C++拾遗(六)——复制控制

年前忙了几天,到现在才算是有空休息下来.先祝大家新年快乐,心想事成:)我也会发笑脸o.o 这篇博文主要介绍定义一个类型的对象时的复制控制方式,这部分内容之前有一定的了解但又浅尝辄止,始终感觉没能找到要点.年前又拿起书细细品读,算是有了一点新的了解.几天前就想动笔了,一直没时间,拖到现在. 每种类型定义了创建该类型的对象时会发生什么——构造函数定义了该类类型对象的初始化.类型还能控制复制.赋值或撤销该类型的对象时会发生什么——类通过特殊的成员函数:复制构造函数.赋值操作符和析构函数来控制这些行为.

C++之重载String ----- 构造函数、复制控制、重载操作符

本博文 我们通过 重新实现String类 来说明构造函数,复制控制,重载操作符. 一.构造函数(包括析构函数): 1:默认构造函数: 2:用户自己定义的构造函数 注意:当用户自己定义时,也要明确显示默认构造函数,这是因为,当我们没有定义自己的构造函数时,编译器会为我们自动合成一个,而我们定义了构造函数时,编译器默认构造函数改为我们自己定义的.这时就有可能出现错误: 3:析构函数: 具体声明与实现,代码如下: 1 声明部分: 2 String(); 3 String(const char*s);

C++复制控制

1.复制构造函数可用于: (1)根据另一个同类型的对象显示或隐式初始化一个对象 string str1="test";   //隐式 string str2=str1; //显示 str1为先调用string的字符串形参的构造函数,创建一个临时对象,然后,使用string复制构造函数将str1初始化为那个临时对象的副本. (2)复制一个对象,将它作为实参传给一个函数 (3)从函数返回时复制一个对象 当形参为非引用类型的时候,将复制实参的值.类似地,以非引用类型作返回值时,将返回retu

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

面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针/引用转换为基类子对象的指针/引用. 基类类型对象既能够作为独立对象存在,也能够作为派生类对象的一部分而存在,因此,一个基类对象可能是也可能不是一个派生类对象的部分,因此,没有从基类引用(或基类指针)到派生类引用(或派生类指针)的(自己主动)转换. 关于对象类型,尽管一般能够使用派生类型的对象对基类