C++复制构造函数

本文介绍了C++中的复制构造函数及其应用场景,如值传递、对象初始化等。讨论了何时需要自定义复制构造函数,特别是在处理指针或资源分配时。还探讨了浅拷贝和深拷贝的概念,强调了深拷贝在防止资源管理问题上的重要性,并提供了一个深拷贝的示例代码。

C++复制构造函数,一般在一下几种情况中出现

1:对象以值传递的方式传入函数时

2:对象以值传递的的方式从函数返回

3:一个对象通过另一个对象初始化

4:数组:根据数组中的初始化列表初始化数组的时候。

5:容器:初始化顺序容器中的元素

有3种情况必须使用复制构造函数:

A:如果有数据成员是指针

B:要在构造函数中分配其它资源

C:在创建对象是必须做一些另外的工作

对3进行一些解释:

如果一个类中的成员变量有引用数据类型,这时候就要特别注意了,比如在构造函数中是new出来的,那么在复制构造函数中最好还是要new。

下面是一个例子,如果在复制构造函数中不new一个数组,那么就要报错(自己写了释放str的析构函数),这个例子如果在自己写了析构函数来进行释放str的内存,或则想实现深层复制,给str重新分配空间,而不是把地址直接赋值给str,也是必须使用默认构造函数的例子。


#include <iostream>
#include <cstring>
#include "deep_copy.h"
using namespace std;
class DeepCopy{
private:
	int a;
	char *str;
public:
	DeepCopy(int aint,const char *str1):str(0)
	{
		a=aint;
		str=new char[50];
		strcpy(str,str1);
	}
	DeepCopy(const DeepCopy& dc):str(0)
	{

		a=dc.a;
		if(str==NULL)
			str=new char[50];
		strcpy(str,dc.str);
	}
	~DeepCopy()
	{
		cout<<"~deepcopy**********"<<endl;
		if(str!=NULL)
			delete str;
		str=NULL;
	}
	void set(const char* setStr)
	{
		strcpy(str,setStr);
	}
	void show()
	{
		cout<<"a="<<a<<"\nstr="<<str<<endl;
	}
};
int main()
{
	DeepCopy dp(10,"i love you ~~~~~~~");
	dp.show();
	DeepCopy dp1=dp;
	dp1.show();
	cout<<"modify the dp1,check if the dp will change!!! "<<endl;
	dp1.set("i hate you!!!");
	dp.show();
	dp1.show();
}




一下是下面的关于c++深层拷贝的一些问题,我始终觉得有点问题

出处:http://www.cnblogs.com/BlueTzar/articles/1223313.html

对于普通类型的对象来说,它们之间的复制是很简单的,例如:
int a=88;
int b=a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。下面看一个类对象拷贝的简单例子。

#include  < iostream >
using   namespace  std;

class  CExample  {
private:
     
int a;
public:
     CExample(
int b)
     
{ a=b;}
     
void Show ()
     
{
        cout
<<a<<endl;
    }

}
;

int  main()
{
     CExample A(
100);
     CExample B
=A;
     B.Show ();
     
return 0;
}
 

运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象B分配了内存并完成了与对象A的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。下面举例说明拷贝构造函数的工作过程。

#include  < iostream >
using   namespace  std;

class  CExample  {
private:
    
int a;
public:
    CExample(
int b)
    
{ a=b;}
    
    CExample(
const CExample& C)
    
{
        a
=C.a;
    }

    
void Show ()
    
{
        cout
<<a<<endl;
    }

}
;

int  main()
{
    CExample A(
100);
    CExample B
=A;
    B.Show ();
    
return 0;
}
 

CExample(constCExample& C)就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(X& x)。

当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化。

如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝,后面将进行说明。

自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。

浅拷贝和深拷贝

  在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

  深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子。

#include <iostream>
using namespace std;
class CA
{
 public:
  CA(int b,char* cstr)
  {
   a=b;
   str=new char[b];
   strcpy(str,cstr);
  }
  CA(const CA& C)
  {
   a=C.a;
   str=new char[a]; //深拷贝
   if(str!=0)
    strcpy(str,C.str);
  }
  void Show()
  {
   cout<<str<<endl;
  }
  ~CA()
  {
   delete str;
  }
 private:
  int a;
  char *str;
};

int main()
{
 CA A(10,"Hello!");
 CA B=A;
 B.Show();
 return 0;
}

深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源,但复制过程并未复制资源的情况视为浅拷贝。

浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。

        Test(Test &c_t)是自定义的拷贝构造函数,拷贝构造函数的名称必须与类名称一致,函数的形式参数是本类型的一个引用变量,且必须是引用。

当用一个已经初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用,如果你没有自定义拷贝构造函数的时候,系统将会提供给一个默认的拷贝构造函数来完成这个过程,上面代码的复制核心语句就是通过Test(Test &c_t)拷贝构造函数内的p1=c_t.p1;语句完成的。
### C++ 复制构造函数 使用详解 #### 什么是复制构造函数复制构造函数是一种特殊的构造函数,用于通过已存在的对象初始化一个新的对象。它通常在以下情况下被调用: - 当一个对象以值传递方式传入函数时; - 当一个对象以值返回方式从函数返回时; - 当创建一个对象并将其初始化为另一个同类型的对象时。 如果没有显式定义复制构造函数编译器会自动生成一个默认的复制构造函数[^4]。然而,默认的复制构造函数仅执行浅拷贝操作,即简单地将源对象的数据成员逐个赋值给目标对象的数据成员。这可能导致某些问题,特别是在涉及动态内存管理的情况下[^2]。 --- #### 浅拷贝与深拷贝的区别 ##### 浅拷贝 浅拷贝是指仅仅复制指针或其他资源的地址,而不是实际的内容。这意味着两个对象可能会共享同一块动态分配的内存区域。这种行为可能导致未定义的行为,尤其是在析构函数释放内存时发生双重删除的情况[^5]。 ##### 深拷贝 深拷贝则是完全独立地复制整个对象及其所拥有的资源。这样可以确保每个对象都有自己的数据副本,从而避免潜在的风险。 --- #### 自定义复制构造函数的实现方法 为了防止浅拷贝带来的问题,在设计类时应考虑是否需要提供自定义的复制构造函数来完成深拷贝。以下是具体实现的一个示例: ```cpp #include <iostream> using namespace std; class MyClass { public: int* data; // 构造函数 MyClass(int value) : data(new int(value)) {} // 复制构造函数 (深拷贝) MyClass(const MyClass& other) { data = new int(*other.data); cout << "Deep copy performed!" << endl; } // 析构函数 ~MyClass() { delete data; } void display() const { cout << "Value: " << *data << endl; } }; int main() { MyClass obj1(10); MyClass obj2(obj1); // 调用了复制构造函数 obj1.display(); obj2.display(); return 0; } ``` 在这个例子中,`MyClass` 的复制构造函数实现了深拷贝逻辑,确保 `obj1` 和 `obj2` 各自有其独立的动态分配整型变量。 --- #### 编译器生成的默认复制构造函数的特点 如果用户没有定义任何特殊形式的构造函数(如复制构造函数或移动构造函数),则编译器会自动合成一个默认版本。此默认版本会对所有非静态成员逐一进行位级复制。需要注意的是,一旦开发者提供了自定义的复制构造函数编译器就不会再生成默认版本[^3]。 --- #### 示例分析:解决浅拷贝引发的问题 以下是一个典型的因浅拷贝而导致崩溃的例子以及如何修复它的解决方案: ```cpp // 原始代码存在浅拷贝问题 class StringWrapper { private: char* buffer; public: StringWrapper(const char* str) { size_t length = strlen(str); buffer = new char[length + 1]; strcpy(buffer, str); } ~StringWrapper() { delete[] buffer; } // 添加自定义复制构造函数以支持深拷贝 StringWrapper(const StringWrapper& other) { size_t length = strlen(other.buffer); buffer = new char[length + 1]; strcpy(buffer, other.buffer); } void printBuffer() const { cout << buffer << endl; } }; void testCopyConstructor() { StringWrapper original("Hello"); StringWrapper copied(original); original.printBuffer(); // 输出 Hello copied.printBuffer(); // 输出 Hello } int main() { testCopyConstructor(); return 0; } ``` 在此修正后的程序里,新增加了一个复制构造函数用来处理字符串缓冲区的深层复制过程,有效规避了原始方案中的隐患[^5]。 --- #### 总结 C++ 中的复制构造函数是非常重要的概念之一,尤其当涉及到复杂数据结构或者外部资源管理时更是如此。合理运用它可以保障程序的安全性和稳定性;反之,则容易造成难以追踪的错误。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值