开始记录下学习之路 c++ 拷贝构造函数

本文通过示例代码详细解析了C++中构造函数与拷贝构造函数的作用及区别,尤其强调了含有指针成员变量时拷贝构造函数的重要性。
#include <iostream>
using namespace std;


class test
{
public:
	int i;
	test() { cout << "构造函数!"<<endl; }
	~test() { cout << "析构函数!"<<endl; }
	test(const int num)
	{ 
		cout << "重载构造函数 " << endl;
		cout <<num << endl;
		i = num;
	}

};

void main()
{
	test t;
	getchar();
}
<pre name="code" class="cpp"> 大手飞过啦。小菜记录下学习笔记而已。


首先说说以上的代码吧, 执行结果很明显 就是 调用了构造函数 ;
</pre><pre name="code" class="cpp">当如果我们把main函数的内容改为如下
<pre name="code" class="cpp">void main()
{
	test t(100);
	test t1 = t;
	cout << t1.i << endl;
	getchar();
}

此时输出结果又是如何呢? 不妨编译一下跑一次试试 输出结果如下

</pre><pre name="code" class="cpp">重载构造函数
100

很奇怪是吗,实例了2个对象却只调用一次构造函数,这里就是拷贝构造函数了 添加如下代码
<pre name="code" class="cpp">	
test(const test& it)
	{
		i = it.i;
		cout << "复制构造函数" << endl;
	}
然后在跑一次看看结果 

</pre><pre name="code" class="cpp">重载构造函数
复制构造函数
100

好了 没问题了 原来是在这里 ,当在定义一个类对象的同时去给这个对象赋值就会出现这种情况,也就是调用拷贝构造函数了。
那么 我们为什么要在拷贝构造函数里面添加 i = it.i; 呢?   我们不就是想赋值一个类嘛,不改变怎么行呢,是吧。
好吧, 你可能会觉得我在脱裤子放屁多此一举, 恩好, 那么请你看如下代码会出现什么问题呢?
<pre name="code" class="cpp">class test
{
public:
	int *i;
	test()
	{ 
		i = new int[10];
		cout << "构造函数!"<<endl;
	}
	~test()
	{ 
		delete[] i;
		cout << "析构函数!"<<endl; 
	}
	void setNum(const int num)
	{
		i[10] = { num };
	}
};

void main()
{
	test i;
	i.setNum(1);
	test i1 = i;
	getchar();
}

感觉这段代码有问题吗?  好像没啥问题呀。 那好 请跑一个试试?  
void main()
{
	test i;
	i.setNum(1);
	test i1 = i;
	cout << i.i << endl << i1.i << endl;
	getchar();
}
</pre><pre name="code" class="cpp">getcahr之后程序挂了吗? 哈哈, 然后我们把main函数换成这样跑一边看看是怎么回事呢. 
</pre><pre name="code" class="cpp">构造函数!
0080A0A0
0080A0A0
可以看到2个对象的一个成员变量指针指向了同一个地址 这里就是拷贝构造函数的问题了。
拷贝构造函数只会帮你去copy成员变量的值 并不会管你是否是指针或是其他,
并且你在构造函数里面的初始化工作也没有用了,指针i的空间也不会new出来的, 
所以说自己的类里面有一些诸如此类的指针变量的时候,我们需要注意这么一个问题,
不然死在哪里都不知道哟,QAQ~ ;
 


                
<think>好的,用户之前问了构造函数和析构函数的知识,现在又接着问什么时候使用拷贝构造函数。这说明用户可能在深入学习C++的面向对象部分,特别是对象生命周期的管理。用户可能已经了解了构造函数和析构函数的基本概念,现在想深入理解拷贝构造函数的应用场景。 首先,我需要确认用户对拷贝构造函数的理解程度。在之前的回答中,我提到过拷贝构造函数是用同类型对象初始化新对象,默认是浅拷贝。现在用户的问题更具体,所以需要详细解释使用场景。 接下来,用户可能在实际编程中遇到了问题,比如对象复制时出现意外行为,或者资源管理的问题,导致内存泄漏或者指针悬挂。这时候需要拷贝构造函数来进行深拷贝,确保每个对象有自己的资源副本。 常见的应用场景包括: 1. 对象作为函数参数传递时,尤其是按值传递,会调用拷贝构造函数。 2. 函数返回对象时,可能会生成临时对象,这时候也需要拷贝构造函数。 3. 显式地用已有对象初始化新对象,比如通过赋值初始化或者在容器中存储对象。 另外,用户可能不清楚默认的拷贝构造函数和自定义拷贝构造函数的区别。默认的是浅拷贝,如果类中有动态分配的资源,浅拷贝会导致多个对象指向同一块内存,析构时重复释放,引发错误。这时候必须自定义拷贝构造函数,进行深拷贝。 还需要提醒用户注意深拷贝的实现细节,比如正确复制所有成员变量,特别是指针类型。同时,可能需要提到C++11中的移动构造函数,但用户的问题集中在拷贝构造函数,所以暂时不需要展开,但可以稍微提及以区分使用场景。 总结用户的真实需求,他们需要知道在什么情况下必须自己编写拷贝构造函数,以及默认拷贝构造函数可能带来的问题。需要给出明确的例子,说明何时和为何使用拷贝构造函数,帮助用户避免常见的陷阱,比如资源管理错误。</think>### 拷贝构造函数的应用场景详解 拷贝构造函数C++中处理对象复制行为的核心机制,主要用于**使用已有对象初始化新对象**的场景。以下是需要显式定义或使用拷贝构造函数的典型情况: --- #### 一、**必须使用拷贝构造函数的场景** ##### 1. **对象作为参数传递(值传递)** 当对象以**值传递**方式传给函数时,系统会自动调用拷贝构造函数创建参数副本。 ```cpp void processObject(MyClass obj) { // 此处触发拷贝构造函数 // 操作副本对象 } MyClass obj1; processObject(obj1); // 调用拷贝构造函数生成obj的副本 ``` ##### 2. **函数返回对象(值返回)** 函数返回对象时,可能生成临时对象(取决于编译器优化),此时会调用拷贝构造函数。 ```cpp MyClass createObject() { MyClass obj; return obj; // 可能触发拷贝构造函数(生成临时对象) } ``` ##### 3. **显式用对象初始化新对象** 通过**已有对象直接初始化新对象**时,拷贝构造函数会被调用。 ```cpp MyClass obj1; MyClass obj2 = obj1; // 调用拷贝构造函数 MyClass obj3(obj1); // 显式调用拷贝构造函数 ``` --- #### 二、**必须自定义拷贝构造函数的场景** ##### 1. **类包含动态资源(深拷贝需求)** 当类成员包含**指针、文件句柄等动态资源**时,必须自定义拷贝构造函数实现**深拷贝**,避免默认浅拷贝导致的多对象共享同一资源的问题。 **错误示例(浅拷贝):** ```cpp class ShallowArray { public: int* data; ShallowArray(int size) { data = new int[size]; } ~ShallowArray() { delete[] data; } // 未定义拷贝构造函数,默认浅拷贝 }; int main() { ShallowArray arr1(5); ShallowArray arr2 = arr1; // 浅拷贝:arr2.data 和 arr1.data 指向同一内存 // 析构时会导致重复释放内存(崩溃) } ``` **正确示例(深拷贝):** ```cpp class DeepArray { public: int* data; int size; DeepArray(int n) : size(n) { data = new int[size]; } // 自定义拷贝构造函数(深拷贝) DeepArray(const DeepArray& other) : size(other.size) { data = new int[size]; // 新分配内存 memcpy(data, other.data, size * sizeof(int)); // 复制内容 } ~DeepArray() { delete[] data; } }; ``` ##### 2. **需要记录对象复制行为** 某些场景需统计对象复制次数或记录复制操作(如调试、性能分析),需显式定义拷贝构造函数。 ```cpp class Logger { public: static int copyCount; Logger() = default; Logger(const Logger& other) { copyCount++; // 记录拷贝次数 } }; int Logger::copyCount = 0; ``` --- #### 三、**其他关联知识** ##### 1. **默认拷贝构造函数的局限性** - 默认拷贝构造函数是**逐成员浅拷贝**(直接复制成员的值)。 - 对于简单数据类型(`int`, `double` 等)可以直接使用默认拷贝构造函数。 - 对于指针、动态资源等复杂成员,必须自定义深拷贝。 ##### 2. **拷贝构造函数与赋值运算符的区别** | 操作 | 拷贝构造函数 | 赋值运算符 (`operator=`) | |-----------------------|---------------------------|-------------------------------| | **调用时机** | 对象初始化时 | 已存在对象被赋值时 | | **语法形式** | `MyClass obj2 = obj1;` | `obj2 = obj1;` | | **资源处理** | 需处理新对象的资源分配 | 需释放旧资源并复制新资源 | --- ### 总结 **何时使用拷贝构造函数?** 1. 对象按值传递或按值返回时(编译器自动调用)。 2. 显式用已有对象初始化新对象时。 3. **类管理动态资源时**(必须自定义深拷贝)。 **关键原则**: 若类的成员涉及指针、文件句柄、网络连接等需要独占的资源,必须显式定义拷贝构造函数实现深拷贝,否则会导致资源冲突或内存泄漏!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值