笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载

同一个类的对象在内存中有完全相同的结构,如果作为一个整体进行复制(拷贝)是完全可行的。这个拷贝过程只需要拷贝数据成员,而函数成员是共用的(只有一份拷贝)。在建立对象时可用同一类的另一个对象来初始化该对象的存储空间,这时所用的构造函数称为拷贝构造函数。拷贝构造函数也是构造函数的一种,只是与构造函数的形参不同。
如果这里有一个空类型,编译器会自动加上6种函数(手动写了则不会提供)
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片
一个类里面值允许有一个缺省构造函数,否则程序就会报错,如:
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片
拷贝构造函数示例:

#include #include #include using namespace std; class Complex { int Real; int Image; public: Complex() :Real(0), Image(0) {}//缺省的构造函数 Complex(int r,int i):Real(r),Image(i)//带参数的构造函数 { cout << "Create Complex: " << this << endl; } ~Complex() { cout << "Destroy Complex: " << this << endl; } //不加引用&,则会变为无穷递归的形式,设置成值类型,必须要构建对象,而设计成引用,只是当前对象的别名 Complex(const Complex& cx) :Real(cx.Real), Image(cx.Image)//拷贝构造函数 { cout << "Copy Create Complex: " << this << endl; } }; void func(Complex cx) { } int main() { Complex c1; func(c1); Complex c2(c1); //用c1初始化c2 Complex c3 = c1; //Complex c3(c1) Complex c4{ c1 }; return 0; }

运行结果: 笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片

练习1:写拷贝构造函数
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片
即将dt1的年份给dt2,dt1的月份给dt2,dt1的天数给dt2。
练习2:写拷贝构造函数
#include #include #include using namespace std; const int len = 20; class CGoods { private: char Name[len]; int Amount; float Price; float Total; public: CGoods(const char* name, int amount, float price) :Amount(amount), Price(price) { strcpy_s(Name, len, name); Total = amount * price; } CGoods(const CGoods& cx) :Amount(cx.Amount), Price(cx.Price),Total(cx.Total) { strcpy_s(Name, len, cx.Name); } }; int main() { CGoods c1("byd", 12, 560000.00); CGoods c2(c1); return 0; }

运行结果:
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片
空指针和空字符串:
char* p=nullptr; //空指针

p=new char[100];
p[0]='\0'; //空字符串
拷贝构造函数还在另外两个方面使用: 当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象空间中。
三种不同函数的调用代表了三种不同的意图:
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片

深拷贝和浅拷贝: 浅拷贝:同一个字符串有两个指针指向,在运行主函数,调动析构函数时,会出现重复析构。
深拷贝:为了避免重复调用析构函数(重复释放空间),再给字符串申请一个空间,进行拷贝。
具体代码如下:
#include #include #include using namespace std; class Mystring { char* str; public: Mystring(const char* p = nullptr) :str(nullptr) //构造函数 获取堆区资源 { if (p != nullptr) { int n = strlen(p) + 1; //'\0'并不计算在字符串的长度,但是需要计算在空间中 str = new char[n]; strcpy_s(str, n, p); } else { str = new char[1]; *str = '\0'; } } ~Mystring() { delete[]str; str = nullptr; } //Mystring(const Mystring& sx) :str(sx.str) {}// 浅拷贝 缺省拷贝构造函数,析构时,同一个空间会释放两次 Mystring(const Mystring& sx):str(nullptr)//深拷贝,不会重复析构空间 { int n = strlen(sx.str) + 1; str = new char[n]; strcpy_s(str, n, sx.str); } }; int main() { Mystring s1("yhpinghello"); Mystring s2(s1); return 0; }

运行结果:
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片
练习:代码如下
#include #include #include enum { STACK_UNIT_SIZE = 10, STACK_INIT_SIZE = 2 }; using namespace std; class MyStack { int* _data; int _capc; int _top; public: MyStack():_data(nullptr),_capc(STACK_INIT_SIZE),_top(-1)//构造函数 { _data = https://www.it610.com/article/new(nothrow) int[_capc]; if (nullptr == _data) exit(1); } ~MyStack() //析构函数 { delete[]_data; _data = nullptr; _capc = 0; _top = -1; } }; void func(MyStack ms) { } int main() { MyStack ita; func(ita); }

运行结果如下:
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片

程序会崩溃,在运行主函数时,编译器会自动调用浅拷贝函数,则会出现重复析构的情况。
运算符的重载: 下面先来看一段代码:
#include #include #include using namespace std; class Complex { private: int Real; int Image; public: Complex(int r = 0, int i = 0) :Real(r), Image(i) //缺省的构造函数 { cout << "Create Complex: " << this << endl; } ~Complex() { cout << "Destroy Complex: " << this << endl; } Complex(const Complex& cx) :Real(cx.Real), Image(cx.Image) { cout << "Copy Create Complex: " << this << endl; } voidPrint() const { cout << "Real: " << Real << "Image: " << Image << endl; } Complex Add(Complex cx) { int r = this->Real + cx.Real; int i = this->Image + cx.Image; Complex tmp(r, i); return tmp; } }; int main() { Complex c1(1, 2), c2(3, 4), c3; c3 = c1.Add(c2); //c3=Add(&c1,c2) c3.Print(); return 0; }

运行结果如下:
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片

上面代码在程序运行时,一共创建了6个对象,即:c1, c2, c3, this指针,cx副本,tmp,则可以发现创建的个数过多,为了使创建的对象足够小,那么就需要修改Add函数。
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片

加法运算符重载:
笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载
文章图片

注:构造函数,析构函数和拷贝构造函数都不能具有常性(不能加const),
拷贝构造函数是按位拷贝。无论在什么函数(拷贝构造,析构函数,赋值重载......)中,
绝对禁止使用memmove或者memset函数
左值(lvalue):对该值能够取地址 &a,&dx
右值(rvalue): 字面常量称之为右值,如10,不能取地址符
将亡值(xvalue): 在程序中运行时,会产生一个临时变量,则将该临时量称之为将亡值。将亡值不是一个静态变量,只有在程序运行时,才会产生。当将亡值没有取名字时是一个右值(如上面程序中的tmp);有名字的将亡值为左值(如:Complex c4=Complex(5,6) )。

【笔记|C++:拷贝构造函数,深拷贝,浅拷贝以及运算符的重载】

    推荐阅读