C++拷贝构造函数原理解析

一、构造函数

在C++面向对象的学习中,对于构造函数应该并不陌生,有默认的构造函数,也有自定义的构造函数。假设有下面类的框架

1.1 默认构造函数

class Human{
    
    
public:
	void eat();
private:
	//私有变量,必须提供共有get方法才能获取
	string name;
	int age;
};

那么此时打印的时候,因为没有在类中定义构造函数,编译器会自动调用默认构造函数,此构造函数没有参数列表没有函数体Human(){};是一个空的构造函数。如果没有自定义构造函数的话编译器就自动调用该构造函数,如果一旦在程序中自定义了构造函数,默认的构造函数及时作废,也就不会再被调用了

1.2 自定义的默认构造函数

class Human{
    
    
public:
	Human();
	void eat();
	string getName();
	int getAge();
private:
	//私有变量,必须提供共有get方法才能获取
	string name;
	int age;
};
string Human::getName(){
    
    
	return name;
}
int Human::getAge(){
    
    
	return age;
}
void Human::eat(){
    
    
	cout << "eat" << endl;
}
Human::Human(){
    
    
	name = "zhongguo";
	age = 9000;
}

int main(){
    
    
	Human hu;
	cout << hu.getName() << endl;//zhongguo
	cout << hu.getAge() <<endl;//9000
	return 0;
}

自定义了构造函数,但是并没有但参数,一般将这种称之为自定义的默认构造函数,当然称为自定义的构造函数也没有什么错。上面的写法其实也可以合二为一,就是将构造函数Human()的原型和定义放在一起,放在public下面。

class Human{
    
    
public:
	Human(){
    
    
		name = "zhongguo";
		age = 9000;
	}
	void eat();
	string getName();
	int getAge();
private:
	//私有变量,必须提供共有get方法才能获取
	string name;
	int age;
};

1.3 自定义带参数的构造函数

这种方式是最为常见的构造函数的写法,

class Human{
    
    
public:
	Human(string name,int age);
	void eat();
	string getName();
	int getAge();
private:
	//私有变量,必须提供共有get方法才能获取
	string name;
	int age;
};
string Human::getName(){
    
    
	return name;
}
int Human::getAge(){
    
    
	return age;
}
void Human::eat(){
    
    
	cout << "eat" << endl;
}
Human::Human(string name,int age){
    
    
	this->name = name;
	this->age = age;
}

int main(){
    
    
	Human hu("zhongguo",9000);
	cout << hu.getName() << endl;//zhongguo
	cout << hu.getAge() <<endl;//9000
	return 0;
}

二、拷贝构造函数的基本使用

同样我们在程序中没有定义拷贝构造函数的时候,编译器调用的是默认的拷贝构造函数,比如上面的程序中,我们加上 Human hu("zhongguo",1000000);此时Human hu2 = hu,这句就是调用了拷贝构造函数,Human hu3(hu)这句也是。
例如下面的代码:

#include <iostream>
#include <Windows.h>
#include <string>

using namespace std;

// 定义一个“人类”
class Human {
    
    
public:
	Human(int age, int salary);//自定义的有参构造函数
	Human(const Human&);//自定义的拷贝构造函数

	string getName();
	int getAge();
	int getSalary();

private:
	string name = "zhonguo";
	int age = 28;
	int salary;
};

//自定义有参构造函数,并且参数不全
Human::Human(int age, int salary) {
    
    
	cout << "调用自定义的有参构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";
}

//拷贝构造函数都是引用
//执行Human  h2 = h1; 这句时会被调用,man.name 相当于h1.name只是赋值给了h2.name
Human::Human(const Human& man) {
    
    
	cout << "调用自定义的拷贝构造函数" << endl;
	name = man.name;
	age = man.age;
	salary = man.salary;
}


string Human::getName() {
    
    
	return name;
}

int Human::getAge() {
    
    
	return age;
}

int Human::getSalary() {
    
    
	return salary;
}


int main(void) {
    
    
	Human  h1(25, 35000);  // 使用自定义的默认构造函数
	Human  h2 = h1;  // 使用自定义的拷贝构造函数
	//下面h2的三个成员变量的值得打印与h1完全相同
	cout << "==============================" << endl;
	cout << "姓名:" << h2.getName() << endl;
	cout << "年龄: " << h2.getAge() << endl;
	cout << "薪资:" << h2.getSalary() << endl;
	system("pause");
	return 0;
}

上面代码中自定义了一个拷贝构造函数,但其实在这种情况使用默认的拷贝构造函数也是一样的,但有些情况下使用默认的拷贝构造函数是有危险的。

因为自动生成的构造函数(也叫合成的拷贝构造函数)它属于“浅拷贝”,或者叫位拷贝

浅拷贝的情况下,如果一个类的成员是指针的话就会出错

见下面示例:

#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>

using namespace std;

// 定义一个“人类”
class Human {
    
    
public:
	Human(int age, int salary);
	//Human(const Human&);  //不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”

	string getName();
	int getAge();
	int getSalary();
	void setAddr(const char* newAddr);
	const char* getAddr();

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	//成员变量中直接定义一个指针
	char* addr;
};


Human::Human(int age, int salary) {
    
    
	cout << "调用自定义的有参构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";//将name从unknow换成无名

	addr = new char[64];
	//strcpy_s函数是C++中的复制字符串的函数,头文件<string.h>
	strcpy_s(addr, 64, "China");
}

string Human::getName() {
    
    
	return name;
}

int Human::getAge() {
    
    
	return age;
}

int Human::getSalary() {
    
    
	return salary;
}

void Human::setAddr(const char* newAddr) {
    
    
	if (!newAddr) {
    
    
		return;
	}

	strcpy_s(addr, 64, newAddr);
}

const char* Human::getAddr() {
    
    
	return addr;
}
int main(void) {
    
    
	Human  h1(25, 35000);  // 使用自定义的默认构造函数
	Human  h2 = h1;  // 使用自定义的拷贝构造函数

	cout << "h1 addr:" << h1.getAddr() << endl;
	cout << "h2 addr:" << h2.getAddr() << endl;

	h1.setAddr("长沙");

	cout << "h1 addr:" << h1.getAddr() << endl;
	cout << "h2 addr:" << h2.getAddr() << endl;

	system("pause");
	return 0;
}

结果为:

在这里插入图片描述
发现一个什么问题,如果使用自动生成的拷贝构造函数,Human h2 = h1;

如果类中存在指针成员的话,那么就相当于固定在一起了,h1改变会引起h2的改变

C++ strcpy_s和strncpy_s使用方法

解决方案:在自定义的拷贝构造函数中,使用”深拷贝

#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>

using namespace std;

// 定义一个“人类”
class Human {
    
    
public:
	Human(int age, int salary);
	Human(const Human &man);  //如果不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”

	string getName();
	int getAge();
	int getSalary();
	void setAddr(const char* newAddr);
	const char* getAddr();

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	//成员变量中直接定义一个指针
	char* addr;
};
Human::Human(const Human & man) {
    
    
	cout << "调用了自定义的拷贝构造函数" << endl;
	age = man.age;
	name = man.name;
	salary = man.salary;
	//使用自定义的拷贝构造函数进行深拷贝
	addr = new char[64];
	strcpy_s(addr,64,man.addr);
}


Human::Human(int age, int salary) {
    
    
	cout << "调用自定义的有参构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";//将name从unknow换成无名

	addr = new char[64];
	//strcpy_s函数是C++中的复制字符串的函数,头文件<string.h>
	strcpy_s(addr, 64, "China");
}

string Human::getName() {
    
    
	return name;
}

int Human::getAge() {
    
    
	return age;
}

int Human::getSalary() {
    
    
	return salary;
}

const char* Human::getAddr() {
    
    
	return addr;
}
void Human::setAddr(const char* newAddr) {
    
    
	if (!newAddr) {
    
    
		return;
	}
	strcpy_s(addr, 64, newAddr);
}

int main(void) {
    
    
	Human  h1(25, 35000);  // 使用自定义的默认构造函数
	Human  h2 = h1;  // 使用自定义的拷贝构造函数

	cout << "h1 addr:" << h1.getAddr() << endl;
	cout << "h2 addr:" << h2.getAddr() << endl;

	h1.setAddr("长沙");

	cout << "h1 addr:" << h1.getAddr() << endl;
	cout << "h2 addr:" << h2.getAddr() << endl;

	system("pause");
	return 0;
}

在这里插入图片描述
由上面结果能看出,h2的地址并没有发生改变

2.1 浅拷贝和深拷贝(原理及区别)

关于深拷贝和浅拷贝的区别可以根据拷贝构造函数来分析

一般默认的拷贝构造函数使用的就是浅拷贝,它是将原来对象的引用直接复制给新对象了,因为标准的拷贝构造是引用类型嘛,这个时候其实新旧对象因为引用相同所以指向的值是相同的,改变原对象的值新对象的值也会随之改变

而深拷贝是创建了一个新的对象,然后将原对象中各种属性的值拷贝过去了,这个时候如果原对象被重新赋值的话,新对象的值还是保持原来的值不变。

根据我们上面的例子,如果成员对象中存在指针的话,必须要使用深拷贝

浅拷贝
在这里插入图片描述
深拷贝

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42595610/article/details/131598817