【C++】拷贝构造函数

目录

拷贝构造函数

拷贝构造函数的调用

默认的拷贝构造函数

深度拷贝 


看完类的构造函数后,再来了解下拷贝构造函数。

拷贝构造函数

拷贝构造函数是一种特殊的构造函数。

(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;
}

猜你喜欢

转载自blog.csdn.net/u013066730/article/details/84553027