Aoki Job Seeker

拷贝构造函数

2019-03-21
C++

拷贝构造函数



写在前面


  • 默认情况下,C++编译器至少会为我们写的类增加三个函数
    • 默认构造函数(无参,函数体为空)
    • 默认析构函数(无参,函数体为空)
    • 默认拷贝构造函数,对类中非静态成员属性进行简单的值拷贝
  • 如果用户定义了拷贝构造函数,C++不会再提供任何默认的构造函数
  • 如果用户定义了普通构造函数(非拷贝),C++不再提提供默认无参构造,但是会提供默认拷贝构造。

在之前的博文中,我介绍了构造函数和析构函数,这一篇里面,我会对拷贝构造函数单独进行介绍,并介绍深拷贝、浅拷贝等。

1.拷贝构造函数的调用时机


拷贝构造函数的调用时机如下:

  • 用一个对象初始化另一个对象
  • 对象以值传递的方式给函数参数
  • 函数局部对象以值传递的方式从函数返回

下面,我举例说明:

#include <iostream>
using namespace std;
class MyClass
{
public:
	MyClass()
	{
		cout << "默认构造函数" << endl;
	}

	MyClass(int a)
	{
		cout << "有参构造函数" << endl;
	}
	MyClass(const MyClass &m)
	{
		cout << "拷贝构造函数调用" << endl;
	}
	~MyClass()
	{
		cout << "析构函数调用" << endl;
	}
	int ak;
};


void test()
{
	MyClass m1;
	m1.ak = 10;
	MyClass m2(m1);//用已经创建好的对象来初始化新的对象
}


int main()
{
	test();
}

输出结果如下图:

在上面的例子中,我使用了一个已经创建好的对象来初始化一个新的对象。 这也就是拷贝构造函数对的第一个调用时机。
下面是第二个调用时机,即对象以值传递的方式给函数参数。

#include <iostream>
using namespace std;
class MyClass
{
public:
	MyClass()
	{
		cout << "默认构造函数" << endl;
	}

	MyClass(int a)
	{
		cout << "有参构造函数" << endl;
	}
	MyClass(const MyClass &m)
	{
		cout << "拷贝构造函数调用" << endl;
	}
	~MyClass()
	{
		cout << "析构函数调用" << endl;
	}
	int ak;
};

void dowork(MyClass m)//以值传递方式给函数传参
{

}
void test()
{
	MyClass m1;
	m1.ak = 10;
	dowork(m1);
}


int main()
{
	test();
}

输出结果如下图:

最后,是函数局部对象以值传递的方式从函数返回。

#include <iostream>
using namespace std;
class MyClass
{
public:
	MyClass()
	{
		cout << "默认构造函数" << endl;
	}

	MyClass(int a)
	{
		cout << "有参构造函数" << endl;
	}
	MyClass(const MyClass &m)
	{
		cout << "拷贝构造函数调用" << endl;
	}
	~MyClass()
	{
		cout << "析构函数调用" << endl;
	}
	int ak;
};

MyClass dowork()//函数局部对象以值传递的方式从函数返回
{
	MyClass m1;
	return  m1;
}
void test()
{
	MyClass m = dowork();
}


int main()
{
	test();
}

输出结果如下:

2.浅拷贝和深拷贝


浅拷贝
同一个类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝。
一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态内存分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;


class Person
{
public:
	Person()
	{

	}
	Person(const char *name, int age)
	{
		Name = (char*)malloc(strlen(name) + 1);
		strcpy (Name, name);
		Age = age;
	}
	~Person()
	{
		cout << "析构函数调用" << endl;
		if (Name != NULL)
		{
			free(Name);
			Name = NULL;
		}
	}
//系统提供默认拷贝构造函数
	char *Name;
	int Age;
};

void test()
{
	Person p1("Aoki", 20);
	Person p2(p1);


}

int main()
{
	test();
}

输出结果如下:

我们可以看到,错误提示信息中提到了heap.cpp。此外,输出结果中出现了两次析构函数的调用。下面,我尝试尽可能地解释清楚原因。
我们创建了一个对象p1,它有两个属性,一个存放在堆区,一个在栈区,当编译器进行简单的值拷贝时,只是将堆区内容的地址给了新对象p2,接着调用析构函数,释放p1的空间,而存放在堆区的数据也就消失了,紧接着,对象p2也调用析构函数来进行释放。那么,堆区的同一个地址就被释放了两次,引发了异常。图示如下:

这时候,我们就要用到深拷贝了。
深拷贝
当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;


class Person
{
public:
	Person()
	{

	}
	Person(const char *name, int age)
	{
		Name = (char*)malloc(strlen(name) + 1);
		strcpy (Name, name);
		Age = age;
	}
	Person(const Person &p)//深拷贝
	{
		Age = p.Age;
		Name = (char*)malloc(strlen(p.Name) + 1);
		strcpy(Name, p.Name);
	}
	~Person()
	{
		cout << "析构函数调用" << endl;
		if (Name != NULL)
		{
			free(Name);
			Name = NULL;
		}
	}
	char *Name;
	int Age;
};

void test()
{
	Person p1("Aoki", 20);
	Person p2(p1);


}

int main()
{
	test();
}


输出结果如下:







The End



Similar Posts

上一篇 函数重载

下一篇 静态成员

Comments