目录
看完类的构造函数后,再来了解下拷贝构造函数。
拷贝构造函数
拷贝构造函数是一种特殊的构造函数。
(1)它是构造函数,所以函数名就是类名,没有返回值;
(2)它是特殊的构造函数,参数形式是固定的。
拷贝构造函数的调用
拷贝构造函数从来都不会显式调用,而是由编译器隐式调用。
(1)定义对象
Object a;
Object b(a); //或写成 Object b = a;
(2)动态创建对象
Object a;
Object* p = new Object(a);
(3)函数的传值调用
void Test(Object obj)
#include "pch.h"
#include <iostream>
#include "stdio.h"
class Object
{
public:
Object(int a, int b)
{
this->a = a;
this->b = b;
}
Object(const Object& other)
{
printf("in copy constructor... \n");
this->a = other.a;
this->b = other.b;
}
private:
int a;
int b;
};
void Test(Object obj) //这里编译器其实进行了obj = x;
{
}
int main()
{
Object x(1, 2);
Object y(x); //与x的地址不相同
Object* p = new Object(x); //与x,y的地址都不相同
Test(x);
printf("%p\n", p);
printf("%p\n", &x);
printf("%p\n", &y);
delete p;
return 0;
}
注意
(1)构造和赋值的区别
构造: Object a; Object b(a); // 或写作Object b = a; 赋值: Object a(1,2); Object b; b = a;
(2)可以访问同类对象的private成员
在拷贝构造函数,可以访问参数对象的任意成员。因为他们是同类,所以访问不受限制。
Object(const Object& other) { this->a = other.a; }
默认的拷贝构造函数
当你自己没有写拷贝构造函数时,编译器会默认提供一个拷贝构造函数,默认的拷贝动作:将每一个成员逐个拷贝。
所以除非必要,请不要自己加拷贝构造函数。
如果一定要自己写拷贝构造函数,请仔细检查:
(1)所有的成员变量,要依次拷贝所有成员变量,不要遗漏;
(2)调用父类的拷贝构造函数。
#include "pch.h"
#include <iostream>
#include "stdio.h"
class Base
{
public:
int test_base;
};
class Object : public Base
{
public:
Object()
{
a = b = 1;
}
Object(int a, int b)
{
this->a = a;
this->b = b;
}
Object(const Object& other) : Base(other) //如果不加Base(other),那么父类的成员就不会被拷贝
{
printf("in copy constructor... \n");
this->a = other.a;
this->b = other.b;
}
private:
int a;
int b;
};
int main()
{
Object x(1, 2);
x.test_base = 10;
Object y(x);
return 0;
}
既然编译器会默认帮我们加拷贝构造函数,那么为什么还要自己加呢?
只有一种情况,那就是需要深度拷贝的时候才会自己添加拷贝构造函数。
深度拷贝
深度拷贝其实就是我不想将指针直接copy过来,而是用一个新的指针指向copy过来的数据。。
#include "pch.h"
#include <iostream>
#include "stdio.h"
#include "string.h"
class Text
{
public:
Text(const char* str)
{
m_size = strlen(str) + 1;
m_buf = new char[m_size];
strcpy(m_buf, str);
}
~Text()
{
delete [] m_buf;
}
const char* GetText()
{
return m_buf;
}
private:
int m_size;
char* m_buf;
};
int main()
{
Text t1("hello,world");
Text t2(t1);
return 0;
}
上面这段代码,在vs2017下居然能跑通,调试过程中发现t1和t2的m_buf是同一块地址,但是程序好像不能正常结束,这使得我非常困惑。但我依旧认为它是不合理的。具体原因如下:
对象创建:
(1)对象t1.m_buf,指向一块内存;
(2)对象t2拷贝了t1,t2.m_buf指向了同一块内存。
对象析构:
(1)对象t1析构,delete [] m_buf;
(2)对象t2析构,delete [] m_buf;出错,此块内存已经被delete了。
究其根本,应该拷贝其数据,不应该拷贝他的指针。
修改方式一:
#include "pch.h"
#include <iostream>
#include "stdio.h"
#include "string.h"
class Text
{
public:
Text(const char* str)
{
m_size = strlen(str) + 1;
m_buf = new char[m_size];
strcpy(m_buf, str);
}
//后加的拷贝构造函数
Text(const Text& other)
{
m_size = other.m_size;
m_buf = new char[m_size];
strcpy(m_buf, other.m_buf);
}
~Text()
{
delete [] m_buf;
}
const char* GetText()
{
return m_buf;
}
private:
int m_size;
char* m_buf;
};
int main()
{
Text t1("hello,world");
Text t2(t1);
printf("%p \n", &t1);
printf("%p \n", &t2);
return 0;
}
修改方式二:(这种方式有点流氓,直接不让你用拷贝构造函数,然后你只能自己慢慢赋值把)
#include "pch.h"
#include <iostream>
#include "stdio.h"
#include "string.h"
class Text
{
public:
Text(const char* str)
{
m_size = strlen(str) + 1;
m_buf = new char[m_size];
strcpy(m_buf, str);
}
~Text()
{
delete [] m_buf;
}
const char* GetText()
{
return m_buf;
}
private:
Text(const Text& other)
{}
private:
int m_size;
char* m_buf;
};
int main()
{
Text t1("hello,world");
printf("%p \n", &t1);
return 0;
}