拷贝构造函数
同一个类的对象在内存中有完全相同的结构,如果作为一个整体进行复制或称拷贝是完全可行的。
这个拷贝过程只需要拷贝数据成员,而函数成员是共用的(只有一份拷贝)。在建立对象时可用同一类
的另一个对象来初始化该对象的存储空间,这时所用的构造函数称为拷贝构造函数(Copy
Constructor)。
using namespace std;class Empty{private:int value1;int value2;public:Empty() {}//构造函数~Empty() {}//析构函数Empty(const Empty&) {}//拷贝构造函数};
在类中如果没有显式给出拷贝构造函数时,则C++编译器自动给出一个缺省的拷贝构造函数。
using namespace std;class Empty{private:int value1;int value2;public:Empty(int x, int y) :value1(0) {value1 = x; value2 = y; }//构造函数~Empty() {}//析构函数void print()const{cout << "value1= " << value1 << "value2= " << value2 << endl;}};int main(){Empty a(2, 2);Empty b(33, 10);Empty c(0, 0);Empty d(b);d.print();return 0;}
打印结果:
如果我们给出了拷贝构造函数,那么则按函数重载的规律,调用合适的构造函数。
using namespace std;class Empty{private:int value1;int value2;int value3;//const char *a = "hello";public:Empty(int x, int y) :value1(0) {value1 = x; value2 = y; }~Empty() {}Empty(const Empty& x) :value1{3 }, value2{3 }{//value1 = x.value1;//value2 = x.value2;}void print()const{cout << "value1= " << value1 << "value2= " << value2 << endl;}};int main(){Empty a(2, 2);Empty b(33, 10);Empty c(0, 0);Empty d(a);//c = a + b;d.print();return 0;}
打印结果value1=3,value2=3。
class Empty{private:int value1;int value2;public:Empty() {}Empty(int x,int y) :value1(0) {value1 = x; value2 = y; }//构造函数~Empty() {}//析构函数Empty(const Empty&) {}//拷贝构造函数Empty& operator+(const Empty& x) {Empty tmp (x.value1 + value1, x.value2 + value2);return tmp;}//运算符重载函数:等号的赋值重载Empty* operator &() {return this; }//取地址函数const Empty* operator &()const {return this; }//引用运算符的重载:取地址void print()const{cout <<"value1= "<< value1 <<"value1= "<< value2 << endl;}};Empty &fun(int x, int y)//此处的返回值为什么不能是对象{Empty tmp(x,y);return tmp;}int main(){Empty a(2,2);Empty b(3,0);Empty e;e = fun(1234,5678);e.print();return 0;}
using namespace std;#if 1class 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; }void show(){cout << "_Real = " << _Real << " _Image = " << _Image << endl;}Complex operator=(const Complex & cx){if (this != &cx){_Real = cx._Real;_Image = cx._Image;}cout << "opreator =" << endl;return *this;}};int main(){Complex c1(1, 2);Complex c2(c1);Complex c3;c3 = c2;c3.show();return 0;}
下图输出的是 赋值重载函数返回值不加引用的情况:
Complex operator=(const Complex & cx)
而下图是 赋值重载函数返回值加引用的情况:
Complex & operator=(const Complex & cx)
由对比可以发现,当赋值重载函数返回值是对象时,当函数消亡之前,在主函数栈帧中创建一个临时的对象,把这个对象的成员一一赋值给C3后此临时对象就死掉了。
而当赋值重载函数返回值是对象的引用时,在函数退出之前不会创建一个临时对象,只返回对象(此this指针)的地址。
注意,当以引用返回时,一定注意此返回值是在哪儿定义的。当函数的生存期结束不影响此返回对象时,可以以引用返回。
深拷贝和浅拷贝
在这里插入代码片
深赋值和浅赋值
class MyString{char* str;public:MyString(const char* p = nullptr){if (p != nullptr){int n = strlen(p) + 1;str = new char[n];strcpy_s(str, n, p);}else{str = new char[1];str[0] = '\0';}cout << "Create MyString" << this << endl;}~MyString(){delete[]str;str = nullptr;cout << "Destroy MyString: " << this << endl;}MyString(const MyString& s){int n = strlen(s.str) + 1;str = new char[n];strcpy_s(str, n, s.str);cout << "Copy Create MyString: " << this << endl;}void Print() const{cout << str << endl;}MyString & fun(){MyString tmp("hello");return tmp;}int main(){MyString s1;s1.Print();s1 = fun();s1.Print();return 0;}
我们可以看到这里打印的不是hello而是随机值。为什么?
当我们用fun()函数去初始化s1时,函数结束返回时以引用的方式,返回的是一个地址,当函数结束,栈帧归还给系统。再次调用时这个地址就是一个悬空指针,所以打印随机值。