Notas resumidas sobre clases y objetos

  • 1. Seis funciones miembro predeterminadas de la clase.
  • 2.Constructor
  • 3. Destructor
  • 4.Copiar constructor
  • 5. Sobrecarga del operador de asignación
  • 6.función miembro constante
  • 7. Sobrecarga de operadores de toma de direcciones y de toma de direcciones constante

    1. Seis funciones miembro predeterminadas de una clase.
    Si no hay miembros en una clase, se denomina clase vacía para abreviar.
    ¿Realmente no hay nada en una clase vacía? No, cuando no se escribe nada en ninguna clase, el compilador generará automáticamente 6 funciones miembro predeterminadas. Función miembro predeterminada; si el usuario no la muestra explícitamente, la función miembro generada por el compilador se denomina función miembro predeterminada.


    2. Constructor
    2.1 Concepto
    Para la siguiente clase de Fecha:

    class Date
    {
    public:
    	void Init(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 d1;
    	d1.Init(2022, 9, 30);
    	d1.Print();
    
    	Date d2;
    	d2.Init(2022, 10, 1);
    	d2.Print();
    	return 0;
    }
    

     Para la clase Date, puede establecer la fecha del objeto a través del método público Init. Sin embargo, sería un poco problemático llamar a este método para establecer la información cada vez que se crea el objeto. ¿Se puede configurar la información cuando se crea el objeto? ¿es creado?
    El constructor es una función miembro especial con el mismo nombre que el nombre de la clase. El compilador la llama automáticamente al crear un objeto de tipo clase para garantizar que cada miembro de datos tenga un valor inicial apropiado y solo se use durante todo el ciclo de vida de el objeto Llamado una vez.

    2.2.Características

    El constructor es una función miembro especial. Cabe señalar que aunque el nombre del constructor se llama construcción, la tarea principal del constructor no es abrir espacio para crear objetos, sino inicializar los objetos.
    Las características son las siguientes:
      1. El nombre de la función y el nombre de la clase son los mismos.
      2. Sin valor de retorno.
      3. El compilador llama automáticamente al constructor correspondiente cuando se crea una instancia del objeto.
      4. Los constructores pueden sobrecargarse

  • class Date
    {
    public:
    	//1.无参的构造函数
    	Date()
    	{}
    	//2.带参数的构造函数
    	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;
    };
    
    void TestDate()
    {
    	Date d1;//调用无参的构造函数,
    	//如果调用无参的构造函数,后面不要跟括号,否则就成了函数声明
    	Date d2(2020, 9, 30);//调用带参的构造函数-
    }
    

    5. Si no hay una definición explícita del constructor en la clase, el compilador de C ++ generará un constructor sin parámetros. Una vez que el usuario muestre la definición, el compilador ya no la generará.
    6. Con respecto al constructor predeterminado generado por el compilador de forma predeterminada, si no implementamos el constructor predeterminado, el compilador lo generará automáticamente, pero el constructor predeterminado generado por el compilador no procesa los tipos integrados y llama a su constructor predeterminado. para tipos personalizados. .

  • class Time
    {
    public:
    	Time()
    	{
    		cout << "Time()" << endl;
    		_hour = 0;
    		_minute = 0;
    		_second = 0;
    	}
    
    private:
    	int _hour;
    	int _minute;
    	int _second;
    };
    
    class Date
    {
    private:
    	//内置类型
    	int _year;
    	int _month;
    	int _day;
    	//自定义类型
    	Time _t;
    };
    
    int main()
    
    {
    	Date d;
    	return 0;
    }
    

    Nota: En C++ 11, se ha aplicado un parche para el defecto de que los miembros de tipo integrados no están inicializados, es decir, a las variables de miembro de tipo integrado se les puede dar un valor predeterminado cuando se declaran en la clase.

  • class Time
    {
    public:
    	Time()
    	{
    		cout << "Time()" << endl;
    		_hour = 0;
    		_minute = 0;
    		_second = 0;
    	}
    
    private:
    	int _hour;
    	int _minute;
    	int _second;
    };
    
    class Date
    {
    private:
    	//内置类型
    	int _year = 2020;
    	int _month = 9;
    	int _day = 30;
    	//自定义类型
    	Time _t;
    };
    
    int main()
    {
    	Date d;
    	return 0;
    }
    

    7. El constructor sin parámetros y el constructor totalmente predeterminado se denominan constructores predeterminados y solo puede haber un constructor predeterminado.
    Nota: Los constructores sin argumentos, los constructores predeterminados y los constructores generados por el compilador pueden considerarse constructores predeterminados.

  • 3. Destructor
    3.1 Concepto
    El destructor es lo opuesto al constructor: el destructor no completa la destrucción del objeto y el compilador completa la destrucción del objeto local. Cuando se destruye el objeto, se llama automáticamente al destructor para completar la limpieza de los recursos del objeto.

    3.2 Características
    El destructor es una función miembro especial con las siguientes características:
      1. El nombre del destructor está precedido por el carácter ~ antes del nombre de la clase.
      2. Sin parámetros, sin tipo de valor de retorno.
      3. Una clase solo puede tener un destructor, si no se muestra ninguna definición, el sistema generará automáticamente un constructor predeterminado. Nota: los destructores no se pueden sobrecargar.
      4. Cuando finalice el ciclo de vida del objeto, el sistema de compilación C ++ llamará automáticamente al destructor.
     

    typedef int DataType;
    class Stack
    {
    public:
    	Stack(int capacity = 4)
    	{
    		_a = (int*)malloc(sizeof(int) * capacity);
    		if (_a == nullptr)
    		{
    			perror("malloc is fail\n");
    			exit(-1);
    		}
    		_top = 0;
    		_capacity = 4;
    	}
    	void Push(DataType data)
    	{
    		_a[_top] = data;
    		++_top;
    	}
    	~Stack()
    	{
    		if (_a)
    		{
    			free(_a);
    			_a = nullptr;
    		}
    		_top = 0;
    		_capacity = 0;
    	}
    private:
    	int* _a;
    	int _top;
    	int _capacity;
    };
    
    int main()
    {
    	Stack st;
    	st.Push(1);
    	st.Push(2);
    	st.Push(3);
    	st.Push(4);
    
    	return 0;
    }
    

    5. Con respecto al destructor generado automáticamente por el compilador, ¿logrará algo? Como puede ver en el programa siguiente, el destructor predeterminado generado por el compilador llama a su destructor para un miembro de tipo personalizado.

  • class Time
    {
    public:
    	~Time()
    	{
    		cout << "Time()" << endl;
    	}
    private:
    	int _hour;
    	int _minute;
    	int _seconds;
    };
    class Date
    {
    private:
        //对于自定义类型,系统会调用它的析构函数,对于内置类型,系统会自动回收空间
    	int _year = 2022;
    	int _month = 9;
    	int _day = 30;
    	Time _t;
    };
    
    int main()
    {
    	Date d1;
    	return 0;
    }
    

    6. Si no se solicita ningún recurso en la clase, no es necesario escribir el destructor y se puede utilizar directamente el destructor predeterminado del compilador. Por ejemplo, la clase Date; cuando hay una aplicación de recursos, debe escribirse; de ​​lo contrario, provocará una pérdida de recursos, como la clase Stack.

    4. Constructor de copia
    4.1.Constructor
    de copia de concepto: solo un único parámetro formal, que es una referencia al objeto de este tipo de clase (generalmente decorado con const), y el compilador lo crea automáticamente al crear un nuevo objeto con una clase existente. tipo transferencia de objeto.

    4.2 Características
    1. El constructor de copia es una forma sobrecargada del constructor.
    2. Solo hay un parámetro del constructor de copia y debe ser una referencia a un objeto de tipo clase.Si se utiliza el método de paso de valor, el compilador informará un error directamente, porque provocará infinitas llamadas recursivas.
     

    class Date
    {
    public:
    	Date(int year = 2022, int month = 10, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	//Date(const Date d)错误写法
    	Date(const Date& d)//正确写法
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    

     3. Si no se muestra la definición, el compilador generará un constructor de copia predeterminado. El objeto constructor de copia predeterminado se copia en orden de bytes según el almacenamiento de memoria. Este tipo de copia se denomina copia superficial o copia de valor.
    Nota: En el constructor de copia predeterminado generado por el compilador, el tipo incorporado se copia directamente en forma de bytes, mientras que el tipo personalizado se copia llamando a su constructor de copia.
    4. El constructor de copia predeterminado generado por el compilador ya ha completado la copia del valor endian, ¿todavía necesito implementarlo explícitamente yo mismo? Por supuesto, una clase como la clase de cita no es necesaria. ¿Qué pasa con las siguientes clases?
     

    typedef int DataType;
    class Stack
    {
    public:
    	Stack(size_t _capacity = 4)
    	{
    		_a = (int*)malloc(sizeof(int) * _capacity);
    		if (_a == nullptr)
    		{
    			perror("malloc is fail\n");
    			exit(-1);
    		}
    		_top = 0;
    		_capacity = 4;
    	}
    	void Push(const DataType& date)
    	{
    		_a[_top] = date;
    		_top++;
    	}
    	~Stack()
    	{
    		if (_a != nullptr)
    		{
    			free(_a);
    			_a = nullptr;
    		}
    		_top = 0;
    		_capacity = 0;
    	}
    private:
    	int* _a;
    	int _top;
    	int _capacity;
    };
    
    int main()
    {
    	Stack st1;
    	st1.Push(1);
    	st1.Push(2);
    	st1.Push(3);
    	st1.Push(4);
    	st1.Push(5);
    
    	Stack st2(st1);
    	return 0;
    }
    

     

    Nota: Si no hay una aplicación de recursos involucrada en la clase, el constructor de copia se puede escribir o no; una vez que involucra la aplicación de recursos, se debe escribir el constructor de copia; de lo contrario, es una copia superficial.
    5. Escenarios típicos de llamada del constructor de copias

  • Crear un nuevo objeto usando un objeto existente
  • El tipo de parámetro de función es un objeto de tipo clase.
  • El tipo de valor de retorno de la función es un objeto de tipo clase.  
    class Date
    {
    public:
    	Date(int year, int minute, int day)
    	{
    		cout << "Date(int,int,int):" << this << endl;
    	}
    	Date(const Date& d)
    	{
    		cout << "Date(const Date& d):" << this << endl;
    	}
    	~Date()
    	{
    		cout << "~Date():" << this << endl;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    Date Test(Date d)
    {
    	Date temp(d);
    	return temp;
    }
    int main()
    {
    	Date d1(2022, 1, 13);
    	Test(d1);
    	return 0;
    }
    

    Insertar descripción de la imagen aquí

    Para mejorar la eficiencia del programa, generalmente intente utilizar tipos de referencia al transferir parámetros a objetos y, al regresar, utilice tantas referencias como sea posible de acuerdo con el escenario real.

    5. Sobrecarga de operadores de asignación
    5.1. Sobrecarga de operadores
    C++ introduce la sobrecarga de operadores para mejorar la legibilidad del código. La sobrecarga de operadores es una función con un nombre de función especial, y también tiene su tipo de valor de retorno, nombre de función y lista de parámetros. El tipo de valor de retorno es lo mismo que La lista de parámetros es similar a una función normal

    El nombre de la función es: El operador de palabra clave debe ir seguido de un símbolo de operador sobrecargado.
    Prototipo de función: operador de tipo de valor de retorno (lista de parámetros)
    Nota:

    No se pueden crear nuevos operadores concatenando otros símbolos, como operador@.
    Los operadores sobrecargados deben tener un parámetro de tipo de clase
    . Para los operadores de tipos integrados, su significado no se puede cambiar.
    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 está oculto this . * :: sizeof ?: Tenga en cuenta que los
    cinco operadores anteriores no se pueden sobrecargar . Esto suele aparecer en exámenes o entrevistas escritas.
     

    class Date
    {
    public:
    	Date(int year = 2022, int month = 10, int day = 9)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    //这里如果我们把运算符重载成全局的话,就需要成员变量是公有的。这样的话,封装性如何保证?
    //其实这个问题可以用友元解决,或者干脆重载成成员函数。
    bool operator==(const Date& d1, const Date& d2)
    {
    	return d1._year == d2._year && d1._month == d2._month
    		&& d1._day == d2._day;
    }
    

     

    5.2 Sobrecarga del operador de asignación

    1. Formato de sobrecarga del operador de asignación

  • Tipo de parámetro: fecha constante y pasar por referencia puede mejorar la eficiencia del paso de parámetros.
  • Tipo de valor de retorno: Fecha y Devolver una referencia puede mejorar la eficiencia de la devolución. El propósito de tener un valor de retorno es respaldar la asignación continua.
  • Compruebe si se asigna un valor a sí mismo.
  • Devuelve *this para agravar el significado de asignación continua.
  • class Date
    {
    public:
    	Date(int year = 2020, int month = 10, int day = 9)
    	{
    		_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;
    };
    

    2. El operador de asignación solo puede sobrecargar funciones miembro de la clase y no puede sobrecargarse en funciones globales.

  • class Date
    {
    public:
    	Date(int year = 2020, int month = 10, int day = 9)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	Date(const Date& d)
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    //赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要两个参数
    Date& operator=(const Date& d1,const Date& d2)
    {
    	if (&d1 != &d2)
    	{
    		d1._year = d2._year;
    		d1._month = d2._month;
    		d1._day = d2._day;
    	}
    	return d1;
    }
    // error c2801: "operator = "必须是非静态成员
    

    Motivo: si el operador de asignación no se puede implementar explícitamente, el compilador generará uno predeterminado. En este momento, si el usuario implementa una sobrecarga de operador global fuera de la clase, entrará en conflicto con el operador de asignación predeterminado generado por el compilador en la clase. Por lo tanto, la sobrecarga del operador de asignación solo puede ser una función miembro de la clase.

  • 3. Cuando el usuario no lo implementa explícitamente, el compilador generará una sobrecarga del operador de asignación predeterminada y lo copiará byte por valor en forma de valor. Nota: Las variables miembro de tipo integradas son directas, mientras que las variables miembro de tipo personalizado deben llamar al operador de asignación de sobrecarga de la clase correspondiente para completar la sobrecarga.
     
    class Time
    {
    public:
    	Time()
    	{
    		_hour = 1;
    		_minute = 1;
    		_second = 1;
    	}
    
    	Time& operator=(const Time& d)
    	{
    		_hour = d._hour;
    		_minute = d._minute;
    		_second = d._second;
    
    		return *this;
    	}
    private:
    	int _hour;
    	int _minute;
    	int _second;
    };
    
    class Date
    {
    private:
    	//内置类型
    	int _year;
    	int _month;
    	int _day;
    	//自定义类型
    	Time _t;
    };
    
    int main()
    {
    	Date d1;
    	Date d2;
    	d1 = d2;
    	return 0;
    }
    

    Ahora que la función sobrecargada del operador de asignación predeterminada generada por el compilador puede completar la copia del valor del orden de bytes, ¿aún necesita implementarla usted mismo? Por supuesto, una clase como la clase de cita no es necesaria. ¿Qué pasa con las siguientes clases? ¿Intentas verificarlo?

  • #include <iostream>
    
    typedef int DataType;
    
    class Stack
    {
    public:
    	Stack(size_t capacity = 10)
    	{
    		_a = (DataType*)malloc(sizeof(DataType)*capacity);
    		if (_a == nullptr)
    		{
    			perror("malloc is fail\n");
    			exit(-1);
    		}
    		_top = 0;
    		_capacity = 0;
    	}
    	void Push(const DataType& data)
    	{
    		_a[_top] = data;
    		_top++;
    	}
    
    	~Stack()
    	{
    		if (_a)
    		{
    			free(_a);
    			_a = nullptr;
    		}
    		_top = 0;
    		_capacity = 0;
    	}
    private:
    	DataType* _a;
    	size_t _top;
    	size_t _capacity;
    };
    
    int main()
    {
    	Stack st1;
    	st1.Push(1);
    	st1.Push(2);
    	st1.Push(3);
    	st1.Push(4);
    	st1.Push(5);
    
    	Stack st2;
    
    	st2 = st1;
    	return 0;
    }
    

    Nota: Si la clase no implica gestión de recursos, no importa si el operador de asignación está implementado, una vez que está involucrada la gestión de recursos, debe implementarse.

  • 4. Sobrecarga de operadores de fechas

    #pragma once
    #include <iostream>
    class Date
    {
    public:
    	// 获取某年某月的天数
    	int GetMonthDay(int year, int month);
    
    	// 全缺省的构造函数
    	Date(int year = 1900, int month = 1, int day = 1);
    
    	// 拷贝构造函数
        // d2(d1)
    	Date(const Date& d);
    
    	// 赋值运算符重载
        // d2 = d3 -> d2.operator=(&d2, d3)
    	Date& operator=(const Date& d);
    
    	// 析构函数
    	~Date();
    
    	// 日期+=天数
    	Date& operator+=(int day);
    
    	// 日期+天数
    	Date operator+(int day);
    
    	// 日期-天数
    	Date operator-(int day);
    
    	// 日期-=天数
    	Date& operator-=(int day);
    
    	// 前置++
    	Date& operator++();
    
    	// 后置++
    	Date operator++(int);
    
    	// 后置--
    	Date operator--(int);
    
    	// 前置--
    	Date& operator--();
    
    	// >运算符重载
    	bool operator>(const Date& d);
    
    	// ==运算符重载
    	bool operator==(const Date& d);
    
    	// >=运算符重载
    	bool operator >= (const Date& d);
    
    	// <运算符重载
    	bool operator < (const Date& d);
    
    	// <=运算符重载
    	bool operator <= (const Date& d);
    
    	// !=运算符重载
    	bool operator != (const Date& d);
    
    	// 日期-日期 返回天数
    	int operator-(const Date& d);
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    

     Fecha.cpp

  • #include "Date.h"
    
    int Date::GetMonthDay(int year, int month)
    {
    	static int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    	if ((year == 2) && ((year % 4 == 0 && year % 100 != 0)
    		|| (year % 400 == 0)))
    	{
    		return a[month] + 1;
    	}
    	return a[month];
    }
    
    Date::Date(int year = 1900, int month = 1, int day = 1)
    {
    	_year = year;
    	_month = month;
    	_day = day;
    }
    
    Date::Date(const Date& d)
    {
    	_year = d._year;
    	_month = d._month;
    	_day = d._day;
    }
    
    Date& Date::operator=(const Date& d)
    {
    	_year = d._year;
    	_month = d._month;
    	_day = d._day;
    }
    
    Date::~Date()
    {
    	_year = 0;
    	_month = 0;
    	_day = 0;
    }
    
    Date& Date::operator+=(int day)
    {
    	if (day < 0)
    	{
    		return *this -= -day;
    	}
    	_day += day;
    	while (_day > GetMonthDay(_year, _month))
    	{
    		_day -= GetMonthDay(_year, _month);
    		++_month;
    		if (_month == 13)
    		{
    			++_year;
    			_month = 1;
    		}
    	}
    	return *this;
    }
    
    Date Date::operator+(int day)
    {
    	Date tmp(*this);
    	tmp += day;
    	return tmp;
    }
    
    Date Date::operator-(int day)
    {
    	Date tmp(*this);
    	tmp -= day;
    	return tmp;
    }
    
    Date& Date::operator-=(int day)
    {
    	if (day < 0)
    	{
    		return *this += -day;
    	}
    	_day -= day;
    	while (_day <= 0)
    	{
    		--_month;
    		if (_month == 0)
    		{
    			--_year;
    			_month = 12;
    		}
    		_day += GetMonthDay(_year, _month);
    	}
    }
    
    Date& Date::operator++()
    {
    	*this += 1;
    	return *this;
    }
    
    Date Date::operator++(int)
    {
    	Date tmp(*this);
    	*this += 1;
    	//return *this - 1;
    	return tmp;
    }
    
    Date Date::operator--(int)
    {
    	Date tmp(*this);
    	*this -= 1;
    	return tmp;
    }
    
    Date& Date::operator--()
    {
    	*this -= 1;
    	return *this;
    }
    
    bool Date::operator>(const Date& d)
    {
    	if (_year > d._year)
    		return true;
    	if (_year == d._year && _month > d._month)
    		return true;
    	if (_year == d._year && _month == d._month && _day > d._day)
    		return true;
    	return false;
    }
    
    bool Date::operator==(const Date& d)
    {
    	return _year == d._year 
    	&& _month == d._month 
    	&& _day == d._day;
    }
    
    bool Date::operator >= (const Date& d)
    {
    	return *this > d || *this == d;
    }
    
    bool Date::operator < (const Date& d)
    {
    	return !(*this >= d);
    }
    
    bool Date::operator <= (const Date& d)
    {
    	return !(*this > d);
    }
    
    bool Date::operator != (const Date& d)
    {
    	return !(*this == d);
    }
    
    int Date::operator-(const Date& d)
    {
    	Date max = *this;
    	Date min = d;
    	int flag = 1;
    	if (*this < d)
    	{
    		max = d;
    		min = *this;
    		flag = -1;
    	}
    	int day = 0;
    	while (min != max)
    	{
    		++day;
    		min += 1;
    	}
    	return day*flag;
    }
    
    

    6.función miembro constante

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

  • class Date
    {
    public:
    	Date(int year,int month,int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	void Print() const
    	{
    		cout << "Print() const" << endl;
    		cout << "year " << _year << endl;
    		cout << "month " << _month << endl;
    		cout << "day " << _day << endl;
    	}
    
    	void Print()
    	{
    		cout << "Print()" << endl;
    		cout << "year " <<  _year << endl;
    		cout << "month " <<  _month << endl;
    		cout << "day " <<  _day << endl;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1(2022,10,11);
    	d1.Print();
    	const Date d2(2022, 10, 12);
    	d2.Print();
    	return 0;
    }
    
    

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

    Estas dos funciones miembro predeterminadas generalmente no necesitan ser definidas por el compilador, el compilador las generará por sí mismo, generalmente las generadas por el compilador de forma predeterminada son suficientes para nuestro uso.

  • class Date
    {
    public:
    	Date* operator&()
    	{
    		return this;
    	}
    
    	const Date* operator&() const
    	{
    		return this;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    

    Continuará…

Supongo que te gusta

Origin blog.csdn.net/jax2730/article/details/127272200
Recomendado
Clasificación