C++中的深浅拷贝
参考博客:https://blog.csdn.net/feitianxuxue/article/details/9275979
一:构造函数:
概念:构造函数是一种随着对象创建而自动被调用的公有成员函数,有且仅在定义对象时自动执行一次,它的主要用途是为对象作初始化(初始化只能有一次)。
特征:
1:函数名和类名相同;
2:无返回值;
3:对象构造时(对象实例化)系统自动调用对应的构造函数;
4:构造函数可以重载;
5:构造函数可以在类中定义,也可以在类外定义;
6:如果类定义中没有给出构造函数,则C++编译器会自动产生一个默认的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成默认的构造函数;
7:无参的构造函数和全默认值的构造函数都认为是默认构造函数,并且默认的构造函数只能有一个。
注意:最好按照成员在类中的声明次序来初始化成员变量(尽量避免用成员来初始化成员)
二:类的拷贝构造函数
概念:创建对象时使用同类对象来进行初始化,拷贝构造函数是特殊的成员函数。
特征:
1:拷贝构造函数其实是一个构造函数的重载。
2:拷贝构造函数的参数只有一个,且必须传引用,使用传值方式会引发无穷递归调用(why?,必须会,答案如下)。
3:若未显示定义,系统会默认生成默认的拷贝构造函数,默认的拷贝构造函数会按照成员的声明顺序依次拷贝类成员进行初始化。
代码:
#include<iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
Date(int year,int month,int day)//构造函数
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)//拷贝构造函数,必须传引用
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Display()const
{
cout << _year<<"_" << _month<<"_" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date date1(2018,8,29);
//下面两种调用拷贝构造函数是等价的。
Date date2(date1);
Date date3 = date1;
cout << "date1" << endl;
date1.Display();
cout << "date2" << endl;
date2.Display();
cout << "date3" << endl;
date3.Display();
system("pause");
return 0;
}
拷贝成功:
三:浅拷贝(慎用)
概念:也称位拷贝,编译器只是将对象中的值采用基本类型值复制的方式拷贝过来,如果对象中管理资源,就会导致多个对象共享同一份资源(资源泄露),当一个对象销毁时就会将该资源释放,而此时另一些对象不知道该资源已经被释放,以为该资源还有效,所以当继续对资源进行操作时,就会发生访问违规。
代码及分析过程
#include "student.h"
#include <iostream>
#include <string.h>
#include<windows.h>
using namespace std;
class String
{
public:
String(const char* str = "")//构造函数
{
cout << "String" << endl;
if (NULL==str)//判断指针是否是空指针
{
str = new char[1];
}
else
{
_str = (new char[strlen(str) + 1]);
memcpy(_str, str, strlen(str) + 1);
}
}
~String()//析构函数
{
cout << "~String" << endl;
if (_str)
{
delete[] _str;//注意new[],delete[]的匹配使用
}
}
private:
char* _str;
};
void Test()
{
String _str1("hello world");
String _str2(_str1);//_str2需要调用String类的拷贝构造函数来创建,由于未显示定义,因此使用默认合成的拷贝构造函数
}
int main()
{
Test();
system("pause");
return 0;
}
注:调用一次构造函数,调用两次析构函数,两个对象的指针成员所指内存相同,这会导致什么问题呢?
_str指针被分配一次内存,但是程序结束时该内存却被释放了两次,会造成内存泄漏问题!
这是由于编译系统在我们没有自己定义拷贝构造函数时,会在拷贝对象时调用默认拷贝构造函数,进行的是浅拷贝!即对指针_str拷贝后会出现两个指针指向同一个内存空间。
在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。
四:深拷贝
概念: 在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间
代码和分析过程
#include "student.h"
#include <iostream>
#include <string.h>
#include<windows.h>
#pragma
using namespace std;
class String
{
public:
String(const char* str = "")//构造函数
{
cout << "String" << endl;
if (NULL==str)
str = "";
_str = new char[strlen(str) + 1];
memcpy(_str, str,strlen(str)+1);
}
String(const String& s)
:_str(NULL)
{
cout << "copy String" << endl;
String strTemp(s._str);
swap(_str, strTemp._str);
}
~String()//析构函数
{
if (_str)//销毁,若为空,则不用销毁
{
cout << "~String" << endl;
delete[] _str;//注意new[],delete[]的匹配使用
}
}
private:
char* _str;
};
void Test()
{
String _str1("hello world");
String _str2(_str1);//_str2需要调用String类的拷贝构造函数来创建,由于未显示定义,因此使用默认合成的拷贝构造函数
}
int main()
{
Test();
system("pause");
return 0;
}
结果:
执行结果:调用两次构造函数,一次自定义拷贝构造函数,两次析构函数。两个对象的指针成员所指内存不同。
注:浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
当对象中存在指针成员时,除了在复制对象时需要考虑自定义拷贝构造函数,还应该考虑以下两种情形:
1.当函数的参数为对象时,实参传递给形参的实际上是实参的一个拷贝对象,系统自动通过拷贝构造函数实现;
2.当函数的返回值为一个对象时,该对象实际上是函数内对象的一个拷贝,用于返回函数调用处。
注:我在这里理解错了深拷贝和浅拷贝与调用析构函数有关系。
析构函数:当一个对象的生命周期结束时,C++编译系统会调用一个成员函数,这个成员函数就是析构函数。(析构函数体内不是删除对象,而是做一些对象删除前的相关清理工作)