Análisis del principio del constructor de copias de C++

1. Constructor

En el aprendizaje orientado a objetos de C++, no debería ser ajeno a los constructores.Hay constructores predeterminados y constructores personalizados. Supongamos que hay un marco de la siguiente clase

1.1 Constructor por defecto

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

Luego, al imprimir en este momento, debido a que no hay un constructor definido en la clase, el compilador llamará automáticamente al constructor predeterminado.Este constructor no tiene lista de parámetros ni cuerpo de función; es un constructor vacío Human(){}. Si no hay un constructor personalizado, el compilador llamará automáticamente al constructor,Una vez que el constructor se personaliza en el programa, el constructor predeterminado se invalidará a tiempo y no se volverá a llamar

1.2 Constructor predeterminado personalizado

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

El constructor está personalizado, pero no hay parámetros. Generalmente, esto se denomina constructor predeterminado personalizado. Por supuesto, no hay nada de malo en llamarlo constructor personalizado. De hecho, el método de escritura anterior también se puede combinar en uno, es decir, juntar el prototipo y la definición del constructor Human() y colocarlo en público.

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

1.3 Personaliza el constructor con parámetros

Este método es la forma más común de escribir constructores.

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

En segundo lugar, el uso básico del constructor de copias.

De manera similar, cuando no definimos un constructor de copia en el programa, el compilador llama al constructor de copia predeterminado. Por ejemplo, en el programa anterior, agregamos; en este momento, esta oración está llamando al constructor de copia, y esta oración también Human hu("zhongguo",1000000)es . Por ejemplo el siguiente código:Human hu2 = huHuman 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;
}

Un constructor de copia está personalizado en el código anterior, pero de hecho es lo mismo usar el constructor de copia predeterminado en este caso, pero en algunos casos es peligroso usar el constructor de copia predeterminado.

Porque el constructor generado automáticamente (también llamado constructor de copia sintética) es una " copia superficial " o una copia de bits

En el caso de una copia superficial, se produce un error si un miembro de una clase es un puntero.

Vea el ejemplo a continuación:

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

El resultado es:

inserte la descripción de la imagen aquí
Encontró un problema, si usa el constructor de copia generado automáticamente, Human h2 = h1;

Si hay un miembro puntero en la clase, es equivalente a estar fijo, y el cambio de h1 provocará el cambio de h2

Cómo usar C++ strcpy_s y strncpy_s

Solución: en el constructor de copias personalizadas, use " copia profunda "

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

inserte la descripción de la imagen aquí
Se puede ver a partir de los resultados anteriores que la dirección de h2 no ha cambiado

2.1 Copia superficial y copia profunda (principio y diferencia)

La diferencia entre copia profunda y copia superficial se puede analizar de acuerdo con el constructor de copias

Por lo general, el constructor de copia predeterminado utiliza una copia superficial, que copia directamente la referencia del objeto original en el nuevo objeto, porque la construcción de copia estándar es un tipo de referencia. En este momento, los objetos antiguo y nuevo apuntan al mismo valor porque el la referencia es la misma Sí, cambiar el valor del objeto original también cambiará el valor del nuevo objeto

Y la copia profunda es crear un nuevo objeto y luego copiar los valores de varios atributos en el objeto original. En este momento, si se reasigna el objeto original, el valor del nuevo objeto permanecerá sin cambios.

De acuerdo con nuestro ejemplo anterior, si hay un puntero en el objeto miembro, se debe usar una copia profunda

copia superficial
inserte la descripción de la imagen aquí
copia profunda

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_42595610/article/details/131598817
Recomendado
Clasificación