C++ 拷贝构造函数

2025 年 8 月 29 日 | 阅读 9 分钟

在 C++ 中,拷贝构造函数是一种构造函数,它通过复制同一类的一个已存在对象的内容来创建一个新对象。每当一个对象被同一个类的另一个对象初始化时,就会调用它。这个过程称为拷贝初始化,它涉及将原始对象的每个数据成员的值赋给新对象。

C++ Copy Constructor

C++ 中,拷贝构造函数执行成员按成员复制,这意味着它会单独复制对象的每个数据成员来创建一个新对象。拷贝构造函数,与 C++ 中的所有其他构造函数一样,没有返回类型,包括 void。

语法

它具有以下语法:

在这个语法中,

  • Class_Name: 代表类的名称。它是该类的构造函数。
  • const Class_Name &object

代表同一类对象的常量引用,该对象被作为参数传递。

简单的 C++ 拷贝构造函数示例

让我们通过一个例子来演示拷贝构造函数。

示例

编译并运行

输出

obj_1 data is: 35
obj_2 data is: 35

说明

在这个例子中,我们定义了一个名为 Copy_construct 的类,其中包含一个默认构造函数和一个拷贝构造函数。拷贝构造函数用于构建一个对象 obj_2,它复制 obj_1 中 d 的值,并打印两个值。

C++ 中拷贝构造函数的特点

C++ 中拷贝构造函数的几个特点如下:

  • 拷贝构造函数通过复制同一类的一个现有实例的数据来初始化新对象。
  • 它接受同一个类的对象的引用作为参数。
  • 拷贝初始化是指利用拷贝构造函数初始化另一个对象的成员的过程。
  • 拷贝构造函数执行成员按成员复制,这意味着它将源对象的每个成员变量复制到新对象。
  • 如果没有显式定义拷贝构造函数,编译器将生成一个默认拷贝构造函数。
  • 程序员可以创建自己的拷贝构造函数来处理特殊的复制行为,如深拷贝或资源管理。
  • 在参数中使用引用可以避免不必要的复制,并确保更好的性能。

拷贝构造函数的类型

C++ 中主要有两种类型的拷贝构造函数。

C++ Copy Constructor

现在,我们将逐一讨论这些拷贝构造函数。

1) 默认拷贝构造函数

在 C++ 中,编译器定义了默认拷贝构造函数。如果用户未定义拷贝构造函数,编译器将提供其构造函数。如果未显式声明拷贝构造函数,C++ 将生成一个默认拷贝构造函数。此默认构造函数将值从原始对象复制到新对象,而无需程序员提供任何特定指令。

C++ 默认拷贝构造函数示例

让我们通过一个例子来演示 C++ 中的默认拷贝构造函数。

示例

编译并运行

输出

Width is: 15
Height is: 58

说明

在此示例中,Rect 类有一个构造函数,用于指定宽度和高度。当 r2 被创建为 r1 的副本时,默认拷贝构造函数会将 r1 的宽度和高度值复制到 r2,通过 getWidth() 和 getHeight() 方法可以访问相同的值。

默认拷贝构造函数的用法

C++ 中默认拷贝构造函数的几个用法如下:

  • 它对于不使用指针或动态内存且成员的浅拷贝就足够了的类很有用。
  • 当不需要特定复制行为时,编译器提供的默认拷贝构造函数可以减少开发工作量并保持代码简洁。

2) C++ 中的用户定义拷贝构造函数

程序员定义用户定义的构造函数。当程序员创建拷贝构造函数来自定义对象复制时,它被称为用户定义或显式拷贝构造函数。

用户定义拷贝构造函数允许程序员确定数据如何从一个对象复制到另一个对象。当默认复制行为不足时,例如使用动态内存或自定义逻辑时,它很有用。

通过仔细定义复制过程,程序员可以确保新对象准确地复制原始对象,从而让他们能够完全控制成员的初始化方式。

C++ 用户定义拷贝构造函数示例

让我们通过一个例子来演示 C++ 中的用户定义拷贝构造函数。

示例

编译并运行

输出

Price of copied product: 500

说明

在此示例中,我们创建了一个 Product 类,该类具有参数化构造函数和用户定义的拷贝构造函数,该拷贝构造函数将价格从一个对象传输到另一个对象。在 main() 函数中,对象 p2 被创建为 p1 的副本,并打印复制的价格值。

用户定义拷贝构造函数的用法

C++ 中用户定义拷贝构造函数的几个用法如下:

  • 它主要用于监视或记录对象何时被复制。
  • 当浅拷贝合适,但在复制操作过程中需要附加行为(如计数和验证)时。

拷贝构造函数的行为

在 C++ 中,拷贝构造函数是一种特殊的构造函数,用于将一个新对象创建为现有对象的副本。拷贝构造函数有两种主要的复制行为。它们如下:

  • 浅拷贝
  • 深拷贝

1) C++ 中的浅拷贝构造函数

在 C++ 中,浅拷贝构造函数允许程序员指定一个对象的值如何复制到另一个对象,包括日志或跟踪等自定义功能。在浅拷贝期间,只有非指针成员被直接复制,因此两个对象的底层资源可能会被共享。

只有默认构造函数才能产生浅拷贝。当不需要动态内存分配时,它非常有用且高效。

C++ 浅拷贝示例

让我们通过一个例子来演示 C++ 中的浅拷贝构造函数。

示例

编译并运行

输出

Copied Item with code: 200

说明

在此示例中,我们使用了一个 shallow_copy 类,其中包含一个整数成员 cod,该成员通过构造函数进行初始化。当使用 s1 创建对象 s2 时(shallow_copy s2 = s1;),将调用拷贝构造函数,该函数将 cod 的值从 s1 复制到 s2。构造函数还会打印一条消息以确认复制。

2) C++ 中的深拷贝构造函数

深拷贝动态分配内存用于复制,然后复制实际值。源和副本都有不同的内存位置。这样,源和副本都是独立的,不会共享相同的内存位置。深拷贝需要我们编写用户定义的构造函数。深拷贝构造函数提供对资源复制的控制,从而实现对象独立性和安全的内存处理。

C++ 深拷贝构造函数示例

让我们通过一个例子来演示 C++ 中的用户定义深拷贝构造函数。

示例

编译并运行

输出

File content: Backup File

说明

在此示例中,我们创建了一个 Deep_Copy 类,该类通过使用深拷贝构造函数来处理字符串的动态内存分配,该构造函数确保每个对象都有自己的内存分配。深拷贝构造函数复制原始对象的内容,这避免了当一个对象被编辑或销毁时出现的共享内存或数据损坏等问题。

何时调用拷贝构造函数?

在 C++ 中,拷贝构造函数通常在以下情况下被调用:

  • 当函数按值返回给定类的对象时。
  • 当对象作为值传递给函数时。
  • 当新对象通过引用同一个类的现有对象来初始化时。
  • 当编译器在表达式求值过程中创建临时对象时。

然而,由于编译器优化(如返回值优化 (RVO)),在某些情况下实际执行拷贝构造函数并不总是得到保证。这些优化允许编译器消除不必要的复制,从而提高性能。

拷贝省略

拷贝省略是 C++ 编译器的一种优化,它消除了对象不必要的复制,特别是在对象返回或函数调用期间。它通过消除不必要的构造函数和析构函数调用来提高性能,这对于处理大型或资源密集型对象特别有用。

C++ 拷贝省略示例

让我们通过一个例子来演示 C++ 中的拷贝省略。

示例

编译并运行

输出

Parameterized constructor
Destructor

说明

在此示例中,我们定义了一个 tpointech 类,该类具有记录其调用的构造函数和析构函数,这使我们能够看到对象的创建和销毁方式。在 main() 函数中,一个 tpointtech 对象按值从函数返回,这可能导致拷贝省略并赋值给另一个对象,从而演示了对象的生命周期行为。

C++ 中拷贝构造函数和赋值运算符(=)的区别

C++ 中拷贝构造函数和赋值运算符之间的几个区别如下:

拷贝构造函数赋值运算符(=)
创建一个新对象作为现有对象的副本。将值从一个现有对象分配给另一个现有对象。
拷贝构造函数的语法
Class_name(const class_name &object_name)
{
// body of the constructor.
}
赋值运算符的语法
Class_name a,b;
b = a;
当使用现有对象初始化新对象时,会调用拷贝构造函数。
对象作为参数传递给函数。
它返回对象。
当我们使用赋值将现有对象分配给新对象时,会调用赋值运算符。
现有对象和新对象共享不同的内存位置。现有对象和新对象共享相同的内存位置。
如果程序员未定义拷贝构造函数,编译器将自动生成隐式默认拷贝构造函数。如果我们不重载 "=" 运算符,将发生按位复制。
当使用现有对象初始化新对象时。当已创建的对象被赋予另一个对象的赋值时。

C++ 拷贝构造函数选择题

1) 在 C++ 中,拷贝构造函数何时被调用?

  1. 当对象按引用传递时。
  2. 当对象按值返回时。
  3. 当使用 = 赋值对象时
  4. 以上都不是。
 

答案: b) 当对象按值返回时。


2) C++ 中的默认拷贝构造函数执行哪种复制?

  1. 深拷贝
  2. 按位复制
  3. 浅拷贝
  4. 无复制
 

答案:c) 浅拷贝


3) 在 C++ 中,哪些情况需要用户定义的拷贝构造函数?

  1. 仅使用基本数据类型时
  2. 当类包含静态数据时
  3. 当类没有构造函数时
  4. 使用动态内存分配时
 

答案:d) 使用动态内存分配时


4) 在 C++ 中,如果一个类使用动态内存但我们没有包含拷贝构造函数,会发生什么?

  1. 内存泄漏或未定义行为
  2. 编译错误
  3. 运行时错误
  4. 没有异常
 

答案:a) 内存泄漏或未定义行为


5) 下列哪项代表 C++ 中拷贝构造函数的正确签名?

  1. ClassName (ClassName obj)
  2. ClassName (ClassName& obj)
  3. ClassName (const ClassName& obj)
  4. ClassName (ClassName * obj)
 

答案:c) ClassName (const ClassName& obj)


下一主题C++ 析构函数