C++深拷贝与浅拷贝~详解与案例分析

**

C++学习记录与总结之——浅拷贝与深拷贝

**
那句话怎么说来着,C++从入门到放弃,学到C++核心编程的内容终于感受到这句话,真有道理~哈哈哈
类与对象有关的知识点太多太多,比较杂,知识点也还要结合内存(如堆、栈、全局区等)等底层内容来理解,很伤脑筋!还是好好加油吧,废话不多说,进入正题。
对于类和对象,C++提供三种默认的函数:
默认构造函数;
默认析构函数;
默认拷贝构造函数;
对于这三个默认构造函数的区别,我相信看到这里的童鞋还是很清楚的,这里便不再多说。
若程序员没有自己写拷贝构造函数,那么系统会提供一个默认的拷贝构造函数,这个拷贝构造函数的传递方式为普通的赋值传递(即值传递)。
初学C++,我们常涉及到的内存分区有:
栈(存储局部变量,如函数参数,子函数等等)
堆(通过new关键词去手动开辟,并且需利用delete关键词去主动释放。简单来说,一个new就要对应一个delete)
全局区(存放全局变量和静态变量,程序结束时系统会自动释放)
其他分区(不多描述)
对于栈区和全局区,遵循先进后出的原则,所有内存的分配和使用都是系统自动的,编程时不会涉及到内存的分配和使用,这两个区均由系统自动管理,理想情况下不会因代码问题出现内存崩溃的情况。
堆区就比较特殊了,既然是由编程人员主动开辟和释放,难免因程序的Bug引起一些错误,如内存溢出,重复释放的问题。
堆区开辟的内存要用一个指针去接收,如下语句:

int a=1;
int *p;
p=new int(a);

前面已说,在定义一个类时,若不自己提供拷贝构造函数,系统提供一个默认的拷贝构造函数,这个拷贝拷贝构造函数的传递方式为值传递。那么当程序员有在堆区开辟内存空间时,若依旧不提供拷贝构造函数,则通过拷贝构造函数创建出的新的对象,它的属性值会与前一个对象的属性值是相同的。具体点,由于有堆区开辟空间,那么上述所说属性值基本属于指针类型。这便意味着,有两个指针,指向同一个堆区的内存地址。当程序运行结束时,系统会自动调用析构函数对堆区内存进行释放,这必然导致同一个堆区内存地址被释放两次,这是绝不允许的,程序直接终止报错。以下为错误代码:

using namespace std;
#include 
#include

class book
{
public:
	int *page;
	string name;
	book()   //无参构造函数
	{
		*page = 0;
		name = " ";
		cout << "默认无参构造" << *page << endl;
	}
	book(int page1, string name)   //有参构造函数
	{
		page = new int(page1);
		this->name = name;
		cout << "有参构造函数调用" << this->name<<*this->page<<endl;
	}
	~book()
	{
		delete page;
		cout << "book的析构函数调用" << endl;
	}
};

void test()
{
	book book1(200,"C++");
	book book2(book1);
	cout << *book2.page << book2.name << endl;

}


int main()
{

	test();

	return 0;
}

运行之后的崩溃界面:
C++深拷贝与浅拷贝~详解与案例分析_第1张图片
C++深拷贝与浅拷贝~详解与案例分析_第2张图片
从上图也可以看出,程序要结束时,第一个析构函数运行成功,但第二个析构未能成功运行。这也侧面反映出内存重复释放了导致出错。
这便是浅拷贝。即构造函数的实现是简单的赋值传递(值传递)
那如何解决浅拷贝带来的问题呢?深拷贝实现。
如有在堆区开辟内存,则应该自己提供拷贝构造函数,通过在堆区开辟新的空间来存储相同的属性值。这么做的结果便是两个指针分别指向两个不同的堆区地址,由此解决内存重复释放的问题。此为深拷贝。
以下为深拷贝示例代码:

using namespace std;
#include 
#include

class book
{
public:
	int* page;
	string name;

	book()
	{
		*page = 0;
		name = " ";
		cout << "默认无参构造" << *page << endl;
	}
	book(int page1, string name)
	{
		page = new int(page1);
		this->name = name;
		cout << "有参构造函数调用" << this->name << *this->page << endl;
	}
	//引用传递
	book(const book& b1)
	{
		page = b1.page;
		page = new int(*page);
	}
	~book()
	{
		if (page != NULL)
		{
			delete page;
			page = NULL;
		}
		cout << "book的析构函数调用" << endl;
	}
};

void test()
{
	book book1(200, "C++");
  //引用传递
	book book2(book1);
	cout << *book2.page << book2.name << endl;

}


int main()
{

	test();

	return 0;
}

上述代码采用引用传递方式,其运行结果为:
C++深拷贝与浅拷贝~详解与案例分析_第3张图片
可以看到,最后两个析构函数均运行成功,程序未报错。
也可改为指针传递方式,代码附上:

using namespace std;
#include 
#include

class book
{
public:
	int* page;
	string name;

	book()
	{
		*page = 0;
		name = " ";
		cout << "默认无参构造" << *page << endl;
	}
	book(int page1, string name)
	{
		page = new int(page1);
		this->name = name;
		cout << "有参构造函数调用" << this->name << *this->page << endl;
	}
	//指针传递
	book(const book *b1)
	{
		page = b1->page;
		page = new int(*page);
	}
	~book()
	{
		if (page != NULL)
		{
			delete page;
			page = NULL;
		}
		cout << "book的析构函数调用" << endl;
	}
};

void test()
{
	book book1(200, "C++");
	//指针传递
	book book2(&book1);
	cout << *book2.page << book2.name << endl;

}


int main()
{

	test();

	return 0;
}

C++深拷贝与浅拷贝~详解与案例分析_第4张图片
运行成功。
奋斗的人们,加油干吧!

你可能感兴趣的:(c++,类,指针)