c++ 复制控制之复制构造函数

什么是复制构造函数?

只有单个形参, 而且该形参是对本类类型对象的引用( 常用const修饰), 这样的构造函数称为复制构造函数.

什么时候使用复制构造函数?

1. 根据另一个同类型的对象显示或隐式初始化一个对象.

2. 复制一个对象, 将它作为实参传给一个函数.

3. 从函数返回时复制一个对象.

4.初始化顺序容器中的元素.

5.根据元素初始化列表初始化数组元素.

 1)对象的定义形式

     c++支持两种初始化形式:  直接初始化和复制初始化. 复制初始化使用=符号, 而直接初始化将初始化式放在圆括号中.

当用于类类型对象时, 初始化的复制形式和直接形式有所不同: 直接初始化直接调用与实参匹配的构造函数, 复制初始化总是调用复制构造函数. 复制初始化 首先使用指定构造函数 创建一个临时对象, 然后用复制构造函数将那个临时对象复制到正在创建的对象.

例如:

string null_book = "9-999-99999-9"; 复制初始化

string dots(10, ‘.‘);  直接初始化

string empty_copy() = string(); 复制初始化

string empty_direct; 直接初始化

  2) 形参与返回值

      当形参为非引用类型时, 将复制实参的值.  以非引用类型作为返回值时, 将返回return语句中的值的副本.

  3)初始化容器元素

      复制构造函数可用于初始化顺序容器中的元素.  例如

vector<string>  svec(15);

编译器首先使用string默认构造函数创建一个临时值来初始化svec, 然后使用复制构造函数将临时值复制到svec的每个元素.

   4) 构造函数与数组元素

      如果使用常规的花括号括注的数组初始化列表来提供显示元素的初始化, 将使用复制初始化来初始化每个元素.根据指定值创建适当类型的元素, 然后使用复制构造函数将该值复制到相应元素.

  合成的复制构造函数

     如果我们没有定义复制构造函数, 编译器就会为我们合成一个. 与合成的默认构造函数不同, 即使我们定义了其他构造函数, 也会合成复制构造函数.  合成复制构造函数的行为是, 执行逐个成员初始化, 将新对象初始化为原对象的副本.

例如:

class Example

{

private:

int num;

std::string str;

}

则合成复制构造函数如下:

Example :: Example(const Example& other) : num( other.num),  str( other.str) {}

定义自己的复制构造函数

对于许多类而言, 合成复制构造函数只完成必要的工作. 只包含类类型成员或内置类型( 但不是指针类型) 成员的类, 无须显示地定义复制构造函数, 也可以复制.

然而, 有些类必须对复制对象时发生的事情加以控制.
这样的类经常有一个数据成员是指针, 或者有成员表示在构造函数中分配的其他资源. 而另一些类在创建新对象时必须做一些特定工作. 这两种情况下, 都必须定义复制构造函数.

  禁止复制

有些类需要完全禁止复制. 例如, iostream 类就不允许复制 .  要做到禁止复制, 我们必须自己定义一个复制构造函数并且把它声明为private, 并且不对它进行定义.

因为如果我们不声明一个复制构造函数, 编译器会合成一个, 把复制构造函数声明为private并且不定义是防止友元和成员进行复制. 通过声明(但不定义) private 复制构造函数, 可以禁止任何复制类类型对象的尝试.

还有一点需要注意的是如果定义了复制构造函数就必须定义一个默认构造函数, 因为一旦我们定义了构造函数编译器就不会合成默认构造函数了.

最后用课本上的两个例题巩固一下.

1. 对下面的类编写一个复制构造函数复制所有成员.  复制pstring指向的对象而不是复制指针.

struct Noname {
  Noname() : pstring ( new std:: string), i(0), d(0) {}
 private:
    std::string *pstring;
    int i;
    double d;
};

答:

Noname::Noname( const Noname& pg)
{
   pstring = new std::string;
   *pstring = *(pg.pstring);
   i= pg.i;
   d= pg.d;
}

2.哪个类可能需要定义一个复制构造函数?

(a) 包含四个float成员的Point3w 类.

(b) Matrix类, 其中, 实际矩阵在构造函数中动态分配, 在析构函数中删除.

(c) Payroll类, 在这个类中为每个对象提供唯一ID.

(d) Word类, 包含一个string和一个以行列位置为元素的vector.

答: (a)不需要, 因为该类中数据成员都是内置类型,没有指针成员, 使用编译器提供的合成复制构造函数即可.

(b) 需要, 涉及指针及内存的动态分配.

(c) 需要, 根据已存在的Payroll对象创建其副本时, 需要提供唯一的ID.

(d)不需要, 编译器会自动为其数据成员调用string和vector的复制构造函数.

时间: 2024-10-10 14:06:38

c++ 复制控制之复制构造函数的相关文章

C++复制控制:拷贝构造函数

一.拷贝构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用.与默认构造函数一样 ,拷贝构造函数可由编译器隐式调用.拷贝构造函数应用的场合为: (1)根据另一个同类型的对象显式或隐式初始化一个对象. (2)复制一个对象将它作为实参传给一个函数. (3)从函数返回时复制一个对象. (4)初始化顺序容器中的元素. (5)根据元素初始化式列表初始化数组元素. 下面分别对以上5点进行说明. 1.对象的定义式. C++支持两种初始化形式:直接初始化和复制初始化.复制初始

C++编译器合成默认构造函数和复制控制成员(拷贝构造函数,赋值操作符,析构函数)的条件

(参考自<深入理解C++对象模型>) ”C++新手一般有两个常见的误解: 任何class如果没有定义default constructor,就会被合成一个出来. 编译器合成出来的default constructor会明确设定class 内每一个data member的默认值.” 现在主要解释第一条为什么是错误的,根据<深入理解C++对象模型>,”default constructor 在需要的时候被编译器产生出来”,以下就是4种”需要的时候”: 1). 该类含有一个成员对象而后者

【C++ Primer】复制控制

十三.复制控制 1. 复制构造函数 类中的成员函数都默认为inline类型,所以即使在类定义体内的函数声明显示定义为inline类型,在进行函数定义时也能够将inline进行省略. // 复制构造函数应该为常量引用类型,如果允许传值参数会造成无限循环调用从而导致内存溢出. CopyConstruct(const CopyConstruct& a){value = a.value;} 复制构造函数可用于初始化顺序容器中的元素,如vector<string> svec(5); 这种方式使用

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

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

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

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

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

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

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

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

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

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

15.4——构造函数与复制控制

构造函数与继承: (1)构造函数和复制控制成员不能继承,每个类定义自己的,要是没定义就用合成的. 对于基类来说: (1)基本没啥影响,若是只希望派生类来使用,可以定义为protected. 对于派生类来说: (1)派生类构造函数除了初始化自己的成员外,还要初始化基类(这个先进行). (2)若是合成的派生类默认构造函数,它将先调用基类的合成默认构造函数,再接着根据常规变量初始化自己的. (3)定义默认构造函数时的初始化列表可以只写派生类的,会自动隐含的调用基类的. (4)不能直接初始化继承类的成员