C++: seis funciones miembro predeterminadas de una clase


Insertar descripción de la imagen aquí

Página de inicio personal: Página de inicio personal
Columna personal: "Estructura de datos" "Lenguaje C" "C++"

Prefacio

Este blog es un resumen del conocimiento de C ++. Conozcamos las seis funciones miembro predeterminadas de una clase.
Insertar descripción de la imagen aquí
A continuación utilizo principalmente la clase de fecha como ejemplo.


1. Constructor

concepto

El constructor es una función miembro especial con el mismo nombre que la clase. El compilador la llama automáticamente al crear un objeto de tipo clase (creando una instancia de la clase) para garantizar que cada miembro de datos tenga un valor inicial apropiado y se utilice en todo el proceso del objeto. de por vida Solo se llama una vez durante el ciclo.

El constructor es una función miembro especial. Cabe señalar que aunque el nombre del constructor se llama constructor, la tarea principal del constructor no es abrir espacio para crear un objeto, sino inicializar el objeto.

#include <iostream>

using namespace std;

class Date
{
    
    
public:
	//构造函数 函数名与类相同 无返回值 
	Date()
	{
    
    
		// 提示:已调用构造函数
		cout << "Date()" << endl;

		_year = 0;
		_month = 0;
		_day = 0;
	}

	void Print()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d;
	d.Print();

	return 0;
}

El resultado es el siguiente:
Insertar descripción de la imagen aquí
el compilador llama automáticamente al constructor al crear un objeto de tipo clase.

característica

  • 1. El nombre de la función es el mismo que el de la clase.
  • 2.Sin valor de retorno
  • 3. El compilador llama automáticamente al constructor predeterminado correspondiente cuando se crea una instancia del objeto.
  • 4. Constructor sin parámetros , todos los constructores predeterminados y los constructores generados por el compilador de forma predeterminada son todos constructores predeterminados (solo puede haber uno)
  • 5. Si no hay un constructor definido explícitamente en la clase, el compilador de C ++ generará automáticamente un constructor predeterminado sin parámetros (los tipos integrados no se procesan y los tipos personalizados llaman al constructor predeterminado correspondiente). Cuando el usuario define explícitamente el constructor, compilar El generador ya no se generará.
  • 6. Los constructores pueden sobrecargarse
  • 7. En C++ 11, a la declaración de variables miembro de tipo integradas en una clase se le puede dar un valor predeterminado (tenemos una lista de inicialización para reemplazarlo)

Ejemplo:

class Date
{
    
    
public:
	//构造函数 函数名与类相同 无返回值 
	// 默认构造函数
	//Date()
	//{
    
    
	//	// 提示:已调用构造函数
	//	cout << "Date()" << endl;

	//	_year = 0;
	//	_month = 0;
	//	_day = 0;
	//}

	// 默认构造函数
	/*Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}*/

	// 非默认构造函数
	/*Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}*/

	void Print()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d;
	d.Print();

	return 0;
}

:Característica 4:
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí


Característica 3:

Insertar descripción de la imagen aquí


Nota :

  • Si utiliza un constructor sin parámetros para crear un objeto, no es necesario que el objeto vaya seguido de () [llamada a función]; de lo contrario, el compilador lo reconocerá como una declaración de función.
  • Si utiliza el constructor predeterminado para crear un objeto sin pasar parámetros, no es necesario que el objeto vaya seguido de () [llamada a función]; de lo contrario, el compilador lo reconocerá como una declaración de función.

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí

2. Destructor

concepto

Destructor : A diferencia de la función del constructor, el destructor no completa la destrucción del objeto en sí. La destrucción de los objetos locales la completa el compilador. Cuando se destruye el objeto, se llamará automáticamente al destructor para completar la destrucción de los recursos en el objeto Trabajo de limpieza (es decir, principalmente destruir recursos aplicados dinámicamente)

class Date
{
    
    
public:
	 
	Date(int year = 0, int month = 0, int day = 0)
	{
    
    
		cout << "Date(int year = 0, int month = 0, int day = 0)" << endl;

		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	// 析构函数
	~Date()
	{
    
    
		cout << "~Date()" << endl;

		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d(2023, 9, 12);
	d.Print();

	return 0;
}

Insertar descripción de la imagen aquí


característica

  • El nombre del destructor está precedido por el carácter ~
  • Sin parámetros, sin tipo de retorno
  • Una clase solo puede tener un destructor, si no está definido explícitamente, el sistema generará automáticamente un destructor predeterminado.
  • Cuando finaliza el ciclo de vida del objeto, el compilador de C++ llamará automáticamente al destructor
  • Para los tipos integrados, el destructor no los procesa. Para tipos integrados, el destructor llamará al destructor correspondiente
  • Si no hay ningún recurso solicitado en la clase, no es necesario escribir el destructor (como la clase de fecha en el ejemplo), y el destructor predeterminado generado por el compilador se usa directamente, pero cuando se aplica un recurso para, se debe escribir el destructor (todos los punteros Los tipos son todos tipos integrados y el destructor generado por el compilador no limpiará el espacio señalado por el puntero); de lo contrario, provocará fugas de recursos.

Entonces, ¿para qué sirve el destructor generado por el compilador?

class A
{
    
    
public:
	A(int a = 0)
	{
    
    
		cout << "A(int a = 0)" << endl;
		_a = a;
	}

	~A()
	{
    
    
		cout << "~A()" << endl;
	}
private:
	int _a;
};

class Date
{
    
    
private:
	// 内置类型
	int _year = 1;
	int _month;
	int _day;

	// 自定义类型
	A _aa;
};

int main()
{
    
    
	Date d;

	return 0;
}

Insertar descripción de la imagen aquí
Como se muestra arriba, el destructor generado automáticamente por el compilador puede llamar automáticamente al destructor correspondiente cuando procesa un tipo personalizado, por lo que para
estructuras de datos anidadas (como dos pilas para simular colas), no necesitamos hacer esto. Escriba el destructor correspondiente para la estructura, simplemente use la generada por el compilador por defecto.

3. Copiar constructor

concepto

Construcción de copia : El constructor de copia es una forma sobrecargada del constructor con un solo parámetro formal. Este parámetro formal es una referencia a un objeto de esta clase (generalmente usado comúnmente con modificación constante). Al crear un nuevo objeto usando una clase existente tipo de objeto Llamado automáticamente por el compilador

class Date
{
    
    
public:
	Date(int year = 0, int month = 0, int day = 0)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
    
    
		cout << "Date(const Date& d)" << endl;

		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Print()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

void func(Date d1)
{
    
    
	cout << "void func(Date d1)" << endl;
}

int main()
{
    
    
	Date d(2023,9,11);
	func(d);

	return 0;
}

Insertar descripción de la imagen aquí


característica

  • El constructor de copia es una forma sobrecargada del constructor.

  • Solo hay un parámetro para el constructor de copia y debe ser una referencia al objeto de clase. Si no es una referencia y se utiliza el método de paso por valor, el compilador informará directamente un error porque esto provocará una recursividad infinita. .

Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
¿Por qué ocurre la recursividad infinita cuando no pasamos parámetros por referencia?

Primero necesitamos conocer los tres escenarios de llamada del constructor de copias.

  • Crear nuevos objetos usando objetos existentes
  • El tipo de parámetro de función es un objeto de tipo clase (crea parámetros formales)
  • El valor de retorno de la función es un objeto de tipo clase (se creará un objeto temporal para guardar el valor de retorno de la función)

Entonces, cuando el parámetro del constructor de copia no es un tipo de referencia:

Insertar descripción de la imagen aquí


  • Si el constructor de copia no está definido explícitamente, el compilador generará un constructor de copia predeterminado, que copia el objeto en orden de bytes (copia superficial) de acuerdo con el almacenamiento de memoria.
  • El constructor de copia generado por el compilador de forma predeterminada es una copia superficial tanto de los tipos integrados como de los tipos personalizados.

Para clases como las clases de fecha que no tienen solicitudes de recursos, simplemente use el constructor de copia generado automáticamente por el compilador.
Para clases con aplicaciones de recursos (pila, cola, tabla de secuencia...), se debe completar la copia profunda, no se puede utilizar la copia superficial

De la siguiente manera:
use la construcción de copia generada automáticamente por el compilador

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí
Esto se debe a que el objeto a1 es una copia superficial del objeto a, por lo que el objeto a._date y el objeto a1._date apuntan al mismo espacio aplicado dinámicamente. Cuando se destruye el objeto a1, se liberará el espacio y cuando se destruye el objeto a , se liberará nuevamente. Liberar este espacio provocará un error.
La forma correcta de escribirlo es completar la copia profunda, de modo que el objeto a1 se aplique a otro espacio y luego copie el contenido señalado por el objeto a en el espacio.

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí


4. Sobrecarga del operador de asignación

Sobrecarga del operador

C ++ introduce la sobrecarga de operadores para mejorar la legibilidad. La sobrecarga de operadores es una función con un nombre de función especial. El nombre de la función es: el
operador de palabra clave va seguido del símbolo del operador que debe sobrecargarse.
El prototipo de función: el operador de tipo de valor de retorno ( lista de parámetros)
es el siguiente: Sobrecargado menos que el símbolo (<) para la clase de fecha

class Date
{
    
    
public:
	Date(int year = 0, int month = 0, int day = 0)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	// 返回值类型  operator操作符  参数
	bool operator<(const Date& d)
	{
    
    
		if (_year < d._year)
		{
    
    
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
    
    
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
    
    
			return true;
		}

		return false;
	}

	void Print()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d1(2023, 9, 12);
	Date d2(2022, 9, 12);

	cout << (d1 < d2) << endl;
	return 0;
}

Aviso:

  • No se pueden crear nuevos operadores vinculando otros símbolos
  • Los parámetros del operador sobrecargados deben tener un tipo de clase
  • Operadores para tipos integrados cuyo significado no se puede cambiar
  • Cuando se sobrecarga como función miembro de una clase, sus parámetros formales parecen ser uno menos que el número de operandos (este puntero está oculto)
  • ( .* / :: / sizeof /? : / . ) Estos 5 operadores no se pueden sobrecargar
  • Para la sobrecarga del operador de prefijo ++, como la clase de fecha (Fecha y operador ++())
  • Para la sobrecarga del operador postfix ++, como la clase de fecha (Fecha y operador ++ (int))

sobrecarga del operador de asignación

Formato de sobrecarga del operador de asignación:

  • Tipo de parámetro: const T&, pasar referencia puede mejorar la eficiencia del paso de parámetros.
  • Tipo de valor de retorno: T&. Devolver una referencia puede mejorar la eficiencia de la devolución. El propósito de devolver un valor es respaldar la asignación continua.
  • Comprueba si te asignas un valor a ti mismo
  • Devolver *this: Para agravar el significado de la asignación continua

Insertar descripción de la imagen aquí


Aviso:

  • El operador de asignación solo se puede sobrecargar como función miembro de la clase y no se puede sobrecargar como función global.

Porque: si el operador de asignación no está definido explícitamente en la clase, el compilador generará automáticamente una función predeterminada. En este momento, entra en conflicto con la función sobrecargada del operador de asignación definida e implementada por el usuario fuera de la clase (debe usar una función amiga o hacer pública la variable miembro), por lo que el operador de asignación sobrecargado solo puede ser una función miembro de clase.

Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí


  • La sobrecarga del operador de asignación generada por el compilador copia los tipos integrados y los tipos personalizados byte a byte por valor. (Los tipos integrados se copian directamente y las variables miembro de tipo personalizado deben llamar al operador de asignación correspondiente para sobrecargar para completar la asignación)

De manera similar a la construcción de copias, para los tipos con solicitudes de recursos, se debe implementar la recuperación profunda de copias para completar la sobrecarga del operador de asignación.

El compilador genera por defecto
Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí

autorrealizado
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí


5. Sobrecarga de operadores de toma de direcciones y de toma de direcciones constante

miembro constante

La función miembro modificada constante se denomina función miembro constante.La función miembro de clase modificada constante en realidad modifica el puntero este implícito en la función miembro, lo que indica que ningún miembro de la clase puede modificarse en la función miembro.

Aviso:

  • Un objeto modificado por const solo puede llamar a funciones miembro constantes, pero no puede llamar a funciones miembro que no hayan sido modificadas llamando a una función constante.
  • Para objetos modificados por const, puede llamar a funciones miembro normales o funciones miembro constantes.
  • Si una función miembro se sobrecarga como función miembro constante, el compilador hará una coincidencia óptima al llamar a la función.
    Insertar descripción de la imagen aquí

Sobrecarga del operador de toma de direcciones y toma constante de direcciones

Por lo general, no es necesario que usted mismo complete estas dos funciones miembro predeterminadas, solo use las generadas por el compilador de manera predeterminada.
Pero si no desea que otros obtengan la dirección del objeto o permita que otros obtengan la dirección especificada, puede implementar estas dos funciones usted mismo.

class A
{
    
    
public:
	A* operator&()
	{
    
    
		return this;
	}

	const A* operator&() const
	{
    
    
		return this;
	}
private:
	int _a;
};

Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí


Resumir

Lo anterior es mi resumen de las seis funciones miembro predeterminadas de la clase. ¡Gracias por el apoyo! ! !
Insertar descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/li209779/article/details/132679663
Recomendado
Clasificación