[C++] Clases y objetos (medio) describen completamente ejemplos de sobrecarga de operadores -- Clase de fecha (Fecha) -- miembros constantes

Tabla de contenido

1. Introducción

2. Constructor predeterminado completo

3. Interfaz de impresión

4. Copiar construcción

5. Sobrecarga del operador de asignación (operador=)

5.1 La sobrecarga de asignaciones es la función miembro predeterminada, formato de sobrecarga:

5.2 La sobrecarga de asignaciones no puede ser una función global

5.3 El compilador genera por defecto

6. Destructor

7, operador>

8, operador ==

9, operador>=

10, operador <

11, operador <=

12、operador!=

13. operador+= (fecha += días)

14. operador+ (fecha + días)

15. operador-= (fecha-=días)

16. operador- (fecha-días)

17. Pre ++, post ++, pre--, post--

18. Fecha - fecha (número de días devueltos)

19. miembros constantes


1. Introducción

En este artículo, implementaremos principalmente las siguientes interfaces:

#include <iostream>
using namespace std;

class Date
{
public:

	// 获取某年某月的天数
	int GetMonthDay(int year, int month) const;
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);
	//打印接口
	void Print() const;


	// 拷贝构造函数
	// d2(d1)
	Date(const Date& d);
	// 赋值运算符重载
	// d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d);
	// 析构函数
	~Date();


	//总结一下:只读函数可以加const,内部不涉及修改成员的都是只读函数
	// >运算符重载
	bool operator>(const Date& d) const;
	// ==运算符重载
	bool operator==(const Date& d) const;
	// >=运算符重载
	bool operator>=(const Date& d) const;
	// <运算符重载
	bool operator<(const Date& d) const;
	// <=运算符重载
	bool operator<=(const Date& d) const;
	// !=运算符重载
	bool operator!=(const Date& d) const;

	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day) const;
	// 日期-天数
	Date operator-(int day) const;
	// 日期-=天数
	Date& operator-=(int day);


	// 函数重载
	// 运算符重载
	// 前置++
	Date& operator++(); //++d1 -> d1.operator()
	// 加一个int参数,进行占位,跟前置++构成函数重载进行区分
	// 后置++
	Date operator++(int); //d1++ -> d1.operator(0)
	// 后置--
	Date operator--(int);
	// 前置--
	Date& operator--();


	// 日期-日期 返回天数
	int operator-(const Date& d) const;

private:

	int _year;
	int _month;
	int _day;

};

Este proyecto lo escribimos en varios archivos, incluidos los siguientes tres archivos:

A continuación, implementaremos las interfaces anteriores una por una:

2. Constructor predeterminado completo

Hay una deficiencia al escribir normalmente el constructor predeterminado completo. En caso de que el mes y el día pasados ​​como parámetros no existan, aunque el objeto instanciado se inicialice, es ilegal, por lo que necesitamos hacer un juicio, aquí Necesitamos escribir un función para el número de días del mes y compararla al construirla.

Aquí primero implementamos la interfaz GetMonthDay:

// 获取某年某月的天数
int Date::GetMonthDay(int year, int month) const
{
	static int monthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

	if(2 == month
		&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		return 29;
	}

	return monthDay[month];
}
// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))// 判断日期是否合法
	{
		cout << "非法日期" << endl;
		exit(-1);
	}
	else
	{
		_year = year;
		_month = month;
		_day = day;
	}
}

Para la interfaz GetMonthDay, escribimos una matriz para almacenar la cantidad de días en cada mes. El febrero predeterminado es 28 días. A continuación, juzgaremos si es para encontrar la cantidad de días en febrero y juzgaremos el año para ver si es un año bisiesto, en año bisiesto devuelve directamente 29, y en otros casos devuelve directamente el número de días correspondientes al mes. Usamos la modificación estática para la matriz, porque esta función se llamará continuamente más adelante, así que la colocamos en el área estática, solo la abrimos una vez, lo que ahorra tiempo, y el tiempo por una vez no es mucho, pero si es un gran número de llamadas, se mejorará en gran medida la eficiencia.

Aquí se nos preguntará por qué agregar una const después de la interfaz GetMonthDay, aquí explicaremos:

3. Interfaz de impresión

No hay nada que hablar sobre la interfaz de impresión, analicémoslo directamente:

//打印接口
void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

La interfaz de impresión usa la modificación const para esto porque es posible que el objeto que pasamos sea modificado por const.Si la interfaz de impresión no agrega const, habrá el problema de ampliar los permisos, lo que resultará en errores. Después de cambiar a const, no hay problema de permisos.

4. Copiar construcción

Si aún no está seguro acerca de la copia, puede hacer clic en el enlace a continuación, que contiene una explicación detallada de la estructura de la copia: haga clic aquí

// 拷贝构造函数
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

5. Sobrecarga del operador de asignación (operador=)

5.1 La sobrecarga de asignaciones es la función miembro predeterminada, formato de sobrecarga:

Tipo de parámetro : const T&, la referencia de paso puede mejorar la eficiencia del paso de parámetros
Tipo de valor de retorno : T&, la referencia de retorno puede mejorar la eficiencia de la devolución, el propósito del valor de retorno es admitir la asignación continua para detectar
si debe asignarse a sí mismo
return *this : para componer asignación continua el significado de

Escribámoslo en formato sobrecargado:

// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)
{
	if (this != &d)// 存在this就是d的情况
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	return *this;
}

5.2 La sobrecarga de asignaciones no puede ser una función global

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

Intentemos escribirlo nosotros mismos:

El compilador vs2019 que uso aquí, el compilador indicará que se informa un error directamente durante la compilación, por lo que la sobrecarga del operador de asignación no puede ser una función global.

5.3 El compilador genera por defecto

Cuando no lo escribimos, el compilador generará automáticamente una función de sobrecarga de copia, pero la función generada de forma predeterminada se asigna directamente al tipo incorporado, y la variable miembro del tipo personalizado debe llamar a la sobrecarga de asignación de clase correspondiente función para completar la tarea.

Por lo tanto, si hay un tipo personalizado (tipo de clase) en la variable miembro, la función de sobrecarga de asignación del tipo personalizado debe ser correcta, lo cual es correcto.

Si dos pilas implementan una cola, la función de sobrecarga de asignación de la cola se puede generar de forma predeterminada, pero la pila debe escribirse sola, porque la pila tiene recursos de aplicación. Si se copia directamente, las dos pilas usan el mismo recurso. En este caso, uno es la pila que se extrae y la pila que se empuja hacia la pila, sin importar si está dentro o fuera, en realidad está operando en la misma pila, lo cual es incorrecto.

Nota: Si la gestión de recursos no está involucrada en la clase, si el operador de asignación puede implementarse o no; una vez que la gestión de recursos está involucrada, debe implementarse.

La diferencia entre asignación y copia: la copia es un objeto existente para inicializar otro objeto que se creará;

La asignación es la copia de dos objetos existentes.

6. Destructor

Si aún no tiene claro cuál es el destructor, puede hacer clic en el siguiente enlace, que contiene una explicación detallada del destructor: haga clic aquí

// 析构函数
Date::~Date()
{
	_year = 0;
	_month = 0;
	_day = 0;
}

7, operador>

A juzgar por fecha > fecha

1. Si es menor que ese año, no hay necesidad de comparar y devolver falso directamente

2. Compare el mismo mes en el mismo año, si el mes es menor que, devuelva falso directamente

3. El año y el mes son iguales, si el día es menor que, devuelve falso

4. Cuando los juicios anteriores no son correctos, se entiende que la fecha anterior es mayor que la última fecha, y devuelve verdadero

// >运算符重载
bool Date::operator>(const Date& d) const
{
	if (_year < d._year)
		return false;
	else if (_year == d._year && _month < d._month)
		return false;
	else if (_year == d._year && _month == d._month && _day < d._day)
		return false;
	else
		return true;
}

8, operador ==

El juicio de fecha == fecha es muy simple. Es correcto que el año, el mes y el día sean iguales. Veamos la implementación del código:

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

El código anterior se puede simplificar aún más:

bool Date::operator==(const Date& d) const
{
	return _year == d._year 
		&& _month == d._month 
		&& _day == d._day;
}

9, operador>=

Para >=, podemos reutilizar los > y == anteriores sin tener que escribir un conjunto de lógica para juzgar.La lógica de >= es mayor o igual que.

Echemos un vistazo al código de implementación:

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

10, operador <

< y >= son la lógica opuesta, por lo que podemos lograrlo negando >=.

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

11, operador <=

<= y > son la lógica opuesta, por lo que podemos darnos cuenta negando >.

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

12、operador!=

!= y == son la lógica opuesta, por lo que podemos lograrlo negando ==.

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

13. operador+= (fecha += días)

+= se modifica sobre la base original, por lo que el implícito this no se puede modificar con const, porque se modifica sobre la base original, por lo que se muestra el cuerpo de la función += y el objeto sigue ahí. referencia, lo que puede reducir la copia.

Graficamos el análisis contra += días:

Código:

// 日期+=天数
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= (-day);
	}

	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		//月进位
		_month++;

		//月满了
		if (13 == _month)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;
}

Vamos a probarlo:

Verifique si el += que escribimos es correcto

14. operador+ (fecha + días)

La diferencia entre + y += es que += es + para el objeto en sí, y + no es un cambio en el objeto en sí. Por lo tanto, debemos crear una instancia de un nuevo objeto, copiar el objeto pasado al nuevo objeto, almacenar el resultado de + días y regresar.

// 日期+天数
Date Date::operator+(int day) const
{
	Date tmp(*this);

	tmp += day;

	return tmp;
}

Aquí podemos reutilizar el código de += después de copiar una copia.

Echemos un vistazo a los resultados del objeto pasado y el objeto después de +:

Aquí podemos ver que + no afecta el resultado de d1.

15. operador-= (fecha-=días)

-= se modifica sobre la base original, por lo que el implícito this no se puede modificar con const, porque se modifica sobre la base original, por lo que se muestra el cuerpo de la función -= y el objeto sigue ahí. Lo devolvemos por referencia, que puede reducir la copia.

Dibujamos un gráfico para analizar el número de -= días:

Código:

// 日期-=天数
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += (-day);
	}

	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (0 == _month)
		{
			_year--;
			_month = 12;
		}

		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

resultado de la operación:

Compáralo para ver si lo hemos implementado correctamente:

16. operador- (fecha-días)

La lógica de - y + es similar, y la diferencia entre - y -= es que -= es - para el objeto en sí, y - no es un cambio en el objeto en sí. Entonces, necesitamos crear una instancia de un nuevo objeto, copiar el objeto pasado al nuevo objeto, almacenar el resultado de - días y regresar.

// 日期-天数
Date Date::operator-(int day) const
{
	Date tmp(*this);

	tmp -= day;

	return tmp;
}

Después de copiar esto aquí, puede reutilizar -= en el tmp copiado.

Vamos a probarlo:

17. Pre ++, post ++, pre--, post--

Para pre-++ y post-++, pre-- y post--, sus nombres de función son los mismos, pero las funciones implementadas son diferentes.¿Cómo podemos distinguir claramente si lo buscamos en la tabla de símbolos?

Para este tipo de problema, C++ tiene sus propias reglas.C ++ estipula que se agrega un parámetro adicional de tipo int cuando la posición posterior ++ está sobrecargada, pero no es necesario pasar este parámetro cuando se llama a la función, y el el compilador lo pasará automáticamente. Trasero: lo mismo ocurre.

De esta manera, se puede realizar la sobrecarga de funciones.Aunque los nombres de las funciones en la tabla de símbolos son los mismos, uno tiene parámetros y el otro no tiene parámetros.

Implementemos estas interfaces de función por separado:

La regla de pre-++ es: primero ++, luego use. Por tanto, el pre-++ equivale a +=1, y el multiplexado +=.

// 前置++
Date& Date::operator++() //++d1 -> d1.operator()
{
	*this += 1;
	return *this;
}

La regla de post ++ es: usar primero, luego ++. Aquí primero hacemos una copia de this, y luego devolvemos el objeto copiado a this+=1, para que podamos usarlo primero, luego ++.

// 后置++
Date Date::operator++(int) //d1++ -> d1.operator(0)
{
	Date tmp(*this);

	*this += 1;

	return tmp;
}

La regla de pre--- es: primero--, luego usar. Por lo tanto, la preposición ++ equivale a -=1, y la multiplexación -=.

// 前置--
Date& Date::operator--()
{
	*this -= 1;

	return *this;
}

La regla posterior es: use primero, luego --. Aquí primero hacemos una copia de this, y luego devolvemos el objeto copiado a this-=1, para que podamos usarlo primero, y luego --.

// 后置--
Date Date::operator--(int)
{
	Date tmp(*this);

	*this -= 1;

	return tmp;
}

18. Fecha - fecha (número de días devueltos)

Esta interfaz es fecha-fecha, aquí hay un pequeño detalle, cuando la fecha pequeña-fecha grande, el valor de retorno es un número negativo, por lo que debemos prestar atención a este detalle, resolvamos la idea de esta interfaz:

1. Usamos el método de hipótesis, suponiendo que se trata de una fecha grande, la asignamos al objeto máximo y el segundo parámetro es una fecha pequeña, la asignamos al objeto mínimo y definimos una bandera para inicializar en 1;

2. Compare, si se trata de una fecha pequeña, reasigne el contenido de los objetos max y min, y asigne el indicador a -1;

3. Defina un contador n, deje que el pequeño persiga al grande, ++min una vez, el contador también ++, salte del bucle después de ponerse al día y devuelva n*flag.

Nota: Usamos pre-++ cuando dejamos que lo pequeño persiga a lo grande, aunque se pueden realizar tanto pre-++ como post-++, pero la interfaz de post-++ debe copiarse dos veces, copie una vez al principio , y el valor de retorno se copiará nuevamente Al copiar una vez, pre-++ puede darse cuenta de las ventajas duales de tiempo y espacio al calcular una gran cantidad de cálculos.

Implementemos el código:

// 日期-日期 返回天数
int Date::operator-(const Date& d) const
{
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (max < min)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	int n = 0;
	while (min != max)
	{
		++n;
		++min;
	}

	return n * flag;
}

prueba:

19. miembros constantes

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

Aquí usamos la interfaz de impresión:

Después de ser modificado con const, no entra en conflicto con la función miembro original, porque constituye una sobrecarga.

Echemos un vistazo a las dos versiones de la interfaz de impresión:

void Date::Print()
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

Para la función de impresión, no hay modificación de los miembros dentro de la función y esta es una función de solo lectura.

Por lo tanto, usamos decoración const, y todavía hay un problema.Cuando nuestro objeto es decoración const, si la función Imprimir no usa decoración const, habrá un problema de amplificación de privilegios al llamar.

Primero cerremos la función Print modificada const:

Echemos un vistazo a la interfaz modificada const:

Resumen: para la misma función, las funciones this modificadas por const y no decoradas constituyen una sobrecarga;

Las funciones que no implican la modificación de miembros son funciones de solo lectura.Después de la modificación const, es más seguro y compatible con objetos con propiedades constantes.

Supongo que te gusta

Origin blog.csdn.net/Ljy_cx_21_4_3/article/details/132128446
Recomendado
Clasificación