c++复制/拷贝构造函数

        在c++中,定义一个空类时,编译器会默认声明6个成员函数,它们分别是

class Empty {
public:
    Empty();                        //默认构造函数
    Empty(const Empty&);            //拷贝构造函数
    ~Empty();                       //析构函数
    Empty& operator=(const Empty&); //赋值运算符
    Empty* operator&();             //取地址运算符
    const Empty* operator&() const; //取地址运算符const
};
        注意 一下,编译器默认合成的析构函数不是虚函数。 

        首先,说一下什么是拷贝构造函数(也可以叫复制构造函数),它是一个特殊的构造函数,具有单个形参(此形参是对该类类型的引用,需要用const修饰,否则会无限循环调用复制构造函数)。一般这几种情况会调用它:1.根据另一个同类型的对象初始化一个对象 2.复制一个对象,将它作为实参传递给一个函数 3.从函数返回时复制一个对象。

        只要我们不显示的定义拷贝构造函数,编译器都会自动合成一个,这时候编译器只是简单对对象中的数据成员进行赋值,也就是浅拷贝。大致的内部实现如代码所示

#include <iostream>

class Test {
public:
	Test(int tmp) {
		this->m1 = tmp;
	}

	//自定义拷贝构造函数
	Test(Test& c) {
		std::cout << "拷贝构造函数" << std::endl;
		m1 = c.m1;
	}

	int m1;
};


int main() {

	Test a(1);
	Test b = a;

	std::cout << b.m1 << std::endl;

	system("pause");

	return 0;
}
        当然大多数情况下,浅拷贝是可以的,但是当类成员含有指针或者其它占有系统资源的变量时,由于浅拷贝只是简单的赋值,会将新对象的指针也指向以前对象已经分配的堆内存空间,如果这时候以前的对象已经析构,新对象的指针就成为野指针,出现运行错误。

        先看下含有指针成员的的类对象浅拷贝代码

#include <iostream>

class Test {
public:
	Test(int* pi) {
		p = new int(100);
		p = pi;
	}

	~Test() {
		if (p != NULL) {
			delete p;
			p = NULL;
		}
	}


	int *p;
};


int main() {

	int i = 5;

	Test a(&i);
	Test b = a;

	std::cout << "对象a中的成员p的地址: " << a.p << std::endl;
	std::cout << "对象b中的成员p的地址: " << b.p << std::endl;

	std::cout << *(a.p) << std::endl;
	std::cout << *(b.p) << std::endl;

	system("pause");

	return 0;
}
输出结果:


从输出结果可以看出对象a和b中的指针成员p指向的是同一块堆内存地址,若a对象先把内存释放后在引用b中的p就会出现野指针的运行时错误。为了防止这种情况发生,我们需要自定义拷贝构造函数,对指针成员不是简单的赋值,需要重新开辟一块内存,将老对象指针指向的数据拷贝进去。这就是深拷贝。

        看看深拷贝的实现:

#include <iostream>

class Test {
public:
	Test(int* pi) {
		p = new int(100);
		p = pi;
	}

	//自定义拷贝构造函数(深拷贝)
	Test(Test& c) {
		std::cout << "拷贝构造函数" << std::endl;
		
		p = new int;
		if (p != NULL) {
			//memcpy(p, c.p, sizeof(c.p));
			*p = *(c.p);
		}
	}
	
	//赋值运算符
	Test& operator=(const Test &rhs) {
		p = new int;
		*p = *(rhs.p);
		return *this;
	}

	~Test() {
		if (p != NULL) {
			delete p;
			p = NULL;
		}
	}


	int *p;
};


int main() {

	int i = 5;

	Test a(&i);
	Test b = a;

	std::cout << "对象a中的成员p的地址: " << a.p << std::endl;
	std::cout << "对象b中的成员p的地址: " << b.p << std::endl;

	std::cout << *(a.p) << std::endl;
	std::cout << *(b.p) << std::endl;

	system("pause");

	return 0;
}

输出结果:


         可以看出对象a和b中的指针保存的不是同一块堆内存地址,它们中的数据是一样的,这样就可以防止出现野指针的运行时错误。由于只是做个简单的测试,代码有点偏向伪码,类属性其实需要定义为private,为了测试方便,直接使用public了。

         需要注意下,一般情况类需要重载赋值运算符,因为我们不一定是直接初始化对象Test b(a),我们可能会这样写Test b = a这种赋值初始化。所以好的建议是自定义了拷贝构造函数后顺便重载赋值运算符。

        也就是说当类中含有指针这种需要占有系统资源的变量时,我们必须自定义拷贝构造函数(深拷贝)。浅拷贝说白了,就是简单赋值,有指针成员时,拷贝的是指针,而深拷贝就是拷贝指针指向的对象。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中,复制构造函数和拷贝构造函数是指用于创建一个新对象并将其初始化为现有对象的构造函数。复制构造函数和拷贝构造函数的作用是相同的,它们都用于将一个对象的值复制给另一个对象。通常情况下,复制构造函数用于通过值传递或返回对象,而拷贝构造函数用于通过引用传递对象。 在引用中提到,当一个类拥有其他资源时(如动态分配内存、打开文件、指向其他数据的指针、网络连接等),默认的拷贝构造函数无法拷贝这些资源,因此必须显式定义一个拷贝构造函数来完成对象的完整拷贝。 在引用中提到,拷贝构造函数可以有多个,并且可以使用不同的参数类型。当一个构造函数的第一个参数是类本身的引用时,它被认为是一个拷贝构造函数。使用引用作为参数的原因有两点:一是为了提高效率,避免不必要的拷贝;二是为了避免死循环,在对象需要以值方式传递时,编译器会生成代码调用拷贝构造函数以生成一个复本,如果没有使用引用作为参数,就会导致死循环。 以下是一个示例代码,展示了如何定义和使用拷贝构造函数: #include <iostream> using namespace std; class Test { public: Test(); ~Test(); Test(const Test& t); Test& operator=(const Test& t); private: int t1; }; Test::Test() { cout << "调用构造函数" << endl; } Test::~Test() { cout << "调用析构函数" << endl; } Test::Test(const Test& t) { cout << "调用拷贝构造函数" << endl; } Test& Test::operator=(const Test& t) { cout << "调用赋值构造函数" << endl; t1 = t.t1; return *this; } int main() { Test t1; Test t2 = t1; // 使用拷贝构造函数创建新对象t2 Test t3; t3 = t1; // 使用赋值构造函数将t1的值赋给t3 return 0; } 输出结果为: 调用构造函数 调用拷贝构造函数 调用构造函数 调用赋值构造函数 调用析构函数 调用析构函数 以上代码中,Test类定义了一个拷贝构造函数和一个赋值构造函数。在main函数中,我们创建了一个t1对象,并使用拷贝构造函数将其值复制给t2对象。然后我们再创建一个t3对象,并使用赋值构造函数将t1的值赋给t3对象。在程序结束时,析构函数会被调用来释放对象占用的资源。 希望以上解答对你有所帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++构造函数(拷贝/复制构造函数)](https://blog.csdn.net/qq_45856289/article/details/106967212)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++复制构造函数和拷贝构造函数](https://blog.csdn.net/hongkangwl/article/details/20462019)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值