Función miembro predeterminada: explica en detalle el funcionamiento implícito de la clase

inserte la descripción de la imagen aquí

1. La función miembro predeterminada de la clase.

La función miembro predeterminada de una clase es una función miembro que se genera automáticamente después de que se define una clase. Si mostramos la definición, la clase no se generará automáticamente.

2. Constructor

En la etapa de aprendizaje de la estructura de datos, desgarramos estructuras de datos como pilas, listas enlazadas y colas. En ese momento, las implementamos en lenguaje C. Cada estructura de datos tiene sus propias funciones de inicialización y destrucción. Estas dos funciones son muy importantes para el uso de estructuras de datos, pero a menudo nos olvidamos de usarlas, por lo que C++ establece una función miembro predeterminada en la clase: el constructor , que funciona como una función de inicialización .
Tiene las siguientes caracteristicas:

  1. El nombre de la función es el mismo que el nombre de la clase.
  2. Sin valor de retorno (incluso no es necesario escribir void )
  3. la función puede estar sobrecargada
  4. Si no hay una definición explícita, se llamará automáticamente cuando se cree una instancia del miembro de la clase.

También hay las siguientes características a tener en cuenta, aquí usamos la clase de fecha clásica para la explicación:

class Date
{
    
    
public:
	Date()//无参的构造函数
	{
    
    
		_year = 2023;
		_month = 5;
		_day = 1;
	}
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}

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

En el código anterior, ya hemos definido claramente el constructor: uno sin parámetros y otro con todos los valores predeterminados . Obviamente, habrá diferencias cuando queramos llamarlo, por lo que estipulamos que al llamar a una función sin parámetros, no Ni siquiera necesito escribir paréntesis :

int mian()
{
    
    
	Date d3(2023,4,5);//调用全缺省的构造函数
	return 0;
}

Si no se define explícitamente ningún constructor en la clase, el compilador generará un constructor predeterminado sin parámetros. Pero el constructor predeterminado generado automáticamente solo inicializará el tipo personalizado y no el tipo incorporado ( diferente en diferentes compiladores ). El tipo integrado es el tipo de datos proporcionado por el lenguaje, como: int/char, etc., y el tipo personalizado es el tipo definido por nosotros mismos, como: struct/class/union, etc.
Este problema surge, si olvidamos definir el constructor en la clase de fecha, porque los miembros de la clase de fecha son todos tipos integrados, el constructor generado automáticamente por el compilador no puede completar la inicialización de los miembros de la clase, por lo que está estipulado en C++11, la variable miembro de tipo integrada puede recibir un valor predeterminado cuando se declara en la clase . También hay cosas a tener en cuenta: los constructores sin argumentos, los constructores predeterminados completos y los constructores que no escribimos para que los genere el compilador de manera predeterminada pueden considerarse constructores predeterminados.
Entonces, ¿cuándo necesitamos escribir el constructor nosotros mismos y cuándo usamos el compilador para generarlo?

  1. En términos generales, cuando hay un tipo integrado y el tipo integrado no tiene un valor predeterminado que cumpla con los requisitos, necesitamos escribir nuestro propio constructor .
  2. Si los miembros son todos tipos personalizados, puede usar las funciones de miembro predeterminadas generadas por el compilador

3. Destructor.

El destructor es la función de destrucción y limpieza para completar la limpieza de los recursos del objeto.Tiene las siguientes características:

  1. El nombre del destructor es el nombre de la clase precedido por ~.
  2. Destructor no tiene valor de retorno ni parámetros.
  3. El destructor no se puede sobrecargar. Una clase solo puede tener un destructor. Si no está definido explícitamente, el compilador lo generará automáticamente.
  4. Cuando finaliza el ciclo de vida del objeto, el sistema llama automáticamente al destructor.

Los destructores son similares a los constructores: los tipos integrados no hacen nada y los tipos personalizados llaman a sus destructores.
A juzgar por esto:

  1. En general, si hay una aplicación dinámica para recursos , es necesario escribir explícitamente el destructor para liberar los recursos.
  2. No hay recursos aplicados dinámicamente, no es necesario escribir destructores
  3. Los miembros que necesitan liberar recursos son todos tipos personalizados y no necesitan escribir destructores

4. Copiar constructor

Cuando queremos crear dos objetos con los mismos miembros, podemos inicializarlos con objetos existentes , lo que requiere el uso del constructor de copias. Solo tiene un parámetro formal que es una referencia al objeto de este tipo . El constructor de copia es una sobrecarga del constructor . Si el parámetro formal no usa una referencia pero se llama por valor, causará infinitas llamadas recursivas, porque el tipo incorporado llama por valor. La copia de la variable intermedia se completa, mientras que la copia del valor del parámetro de tipo personalizado debe ser completada por el constructor de copia .
Si no se define explícitamente, el compilador generará automáticamente un constructor de copia predeterminado: el tipo integrado completará el valor copy/shallow copy y el tipo personalizado llamará a su constructor de copia. Nota: Si la aplicación de recursos no está involucrada
en la clase , el constructor de copias puede escribirse o no. Una vez que la aplicación de recursos está involucrada , el constructor de copias debe escribirse, de lo contrario, es una copia superficial .

Los punteros son tipos incorporados sin importar de qué tipo sean.

5. Sobrecarga del operador de asignación

La sobrecarga de operadores es una función con un nombre de función especial.
El nombre de la función es: la palabra clave operator seguida del símbolo del operador que debe sobrecargarse.
Prototipo de función: operador de tipo de valor de retorno operador (lista de parámetros)
la sobrecarga del operador debe prestar atención a los siguientes puntos:

  1. Un operador sobrecargado debe tener un parámetro de tipo de clase
  2. Operadores utilizados para tipos incorporados, cuyo significado no se puede cambiar, por ejemplo: entero incorporado +, cuyo significado no se puede cambiar
  3. No se pueden crear nuevos operadores concatenando otros símbolos: por ejemplo, operator@
  4. Cuando se sobrecarga como una función miembro de clase, sus parámetros formales parecen ser 1 menos que el número de operandos, porque el primer parámetro de la función miembro es el oculto este
  5. .* sizeof :: ?: .Tenga en cuenta que los 5 operadores anteriores no se pueden sobrecargar.

Un código como el siguiente es una sobrecarga de operadores:

// >运算符重载

	bool operator>(const Date& d)
	{
    
    
		return !(*this <= d);
	}




	// ==运算符重载
	bool operator==(const Date& d)
	{
    
    
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}



	// >=运算符重载
	bool operator >= (const Date& d)
	{
    
    
		return !(*this < d);
	}


	// <运算符重载
	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;
		}
		else
		{
    
    
			return false;
		}
	}


	// <=运算符重载
	bool operator <= (const Date& d)
	{
    
    
		return !(*this > d);
	}


	// !=运算符重载
	bool operator != (const Date& d)
	{
    
    
		return !(*this == d);
	}

Preste atención a la sobrecarga del operador de asignación:

  1. Tipo de parámetro: const T&, pasar por referencia puede mejorar la eficiencia del paso de parámetros
  2. Tipo de valor de retorno: T&, la referencia de retorno puede mejorar la eficiencia del retorno, y el propósito del valor de retorno es apoyar la asignación continua
  3. Comprueba si te asignas un valor
  4. Devuelve *this: para componer el significado de asignación continua
class Date

{
    
     

public :
 Date(int year = 1900, int month = 1, int day = 1)
 {
    
    
 _year = year;
 _month = month;
 _day = day;
 }
 
 Date (const Date& d)
 {
    
    
 _year = d._year;
 _month = d._month;
 _day = d._day;
 }
 
 Date& operator=(const Date& d)
 {
    
    
 	if(this != &d)
 	{
    
    
 		_year = d._year;
 		_month = d._month;
 		_day = d._day;
 	}
 	return *this;
 }

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

¡Hay algunos puntos más a tener en cuenta!
La sobrecarga de asignaciones no se puede escribir como una función global (la función miembro predeterminada no se puede escribir como una función global), pero la declaración y la definición se pueden separar (y no es necesario prestar atención al problema del acceso privado a los miembros de la clase) Regulaciones de C++: ++
un operador de prefijo a.operator() No es necesario agregar parámetros a++ operador de sufijo a.operator(int) necesita agregar parámetros.
Cuando un operador unario está sobrecargado como una función miembro, no hay parámetros en la lista de parámetros y el único operando está oculto en la lista de parámetros en forma de este puntero; cuando está sobrecargado como una función no miembro, el único El operando debe aparecer en la tabla de lista de parámetros.

Supongo que te gusta

Origin blog.csdn.net/qq_43289447/article/details/130455314
Recomendado
Clasificación