Clases y objetos de C++ (3) Sobrecarga de operadores, funciones miembro constantes, implementación de clases de fecha

Prefacio

        Este artículo presenta principalmente el conocimiento de la sobrecarga de operadores y las funciones miembro constantes. La sobrecarga de operadores es una herramienta importante para diseñar clases. Entre ellas, la sobrecarga de operadores de asignación y la sobrecarga de operadores de direcciones son las funciones miembro predeterminadas de la clase. Finalmente, se implementa una comparación basada en sobre este artículo y conocimientos previos sobre clases y objetos Clase de fecha completa.

Los ejemplos de este artículo se basan principalmente en la demostración de clases de fecha:

// 日期类
class Date
{
public:
	// 参数全缺省的构造函数
	Date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 拷贝构造函数
	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}

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

Tabla de contenido

Prefacio

1 Operador sobrecargado

introducir

Operador de asignación sobrecargado

Obtener recarga de dirección

Demostración de la sobrecarga de otros operadores.

Sobrecarga +-, +=, -=

Prefijo++,--

Sufijo++,--

Comparación: >, <, >=, <=, ==, !=

Operador de salida sobrecargado<<

Operadores de entrada sobrecargados >>

Editar

sobrecarga[ ]

2 funciones miembro constantes

3 Clase de fecha de implementación


1 Operador sobrecargado

introducir

        Un operador sobrecargado es una función con un nombre especial, que se compone de la palabra clave operador más el símbolo del operador. La sobrecarga de operadores permite que los operadores se extiendan a tipos de operaciones personalizados, lo que aumenta en gran medida la legibilidad del código.

Aviso:

  • No se pueden crear nuevos operadores, es decir, el operador sólo puede sobrecargar a los operadores existentes.
  • Los operadores sobrecargados deben tener un parámetro de tipo clase.
  • Los operadores de tipos integrados no se pueden redefinir.

Tenga en cuenta los operadores de la siguiente tabla que no se pueden sobrecargar: :: .* . ? :

Ejemplo de sobrecarga de operador, tipo de fecha de sobrecarga == operador:

// 重载成全局函数
bool operator==(const Date d1, const Date d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

Podemos sobrecargar el == global para comparar objetos de clase de fecha, pero luego necesitamos configurar las variables miembro de la clase de fecha como públicas o habilitar la función para acceder a miembros privados a través de amigos (se explica más adelante). Para lograr la encapsulación, este artículo sobrecarga directamente el operador en una función miembro de la clase, prestando atención al puntero this implícito en el parámetro de la función.

// 在类中声明 bool operator == (const Date d);

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

Date d1, d2;
d1 == d2; // 调用运算符函数
d1.operator==(d2); // 显式调用

Operador de asignación sobrecargado

        El operador de asignación es una función llamada operator= . Una clase puede controlar cómo se asignan valores a sus objetos a través del operador de asignación. Al igual que el constructor de copia, si el operador de asignación no está definido en la clase, el compilador sintetizará uno.

        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.

Motivo: si no definimos explícitamente el operador de asignación en la clase, el compilador generará un operador de asignación predeterminado, que entrará en conflicto con el operador de asignación global, por lo que el operador de asignación solo se puede sobrecargar en una función miembro de la clase. .

        Como operador binario, el objeto en el lado izquierdo del operador de asignación está naturalmente vinculado a este parámetro, y el objeto en el lado derecho es un parámetro del mismo tipo . Para cumplir con la asignación continua, el tipo de devolución del operador de asignación debe ser *this . Los parámetros y tipos de retorno son generalmente referencias para mejorar la eficiencia.

Sobrecargue el operador de asignación de la clase de fecha:

// 赋值重载
Date& Date::operator=(const Date& d)
{
	if (this != &d) // 检查是否给自己赋值
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	return *this;
}
int main()
{
	Date d1;
	Date d2(2023, 9, 1);
	d1.Print();

	d1 = d2;   // d1.operator(d2)
	d1.Print();
	return 0;
}

resultado de la operación:

La sobrecarga del operador de asignación generada por el compilador de forma predeterminada es similar al constructor de copia predeterminado, copiando por valor byte a byte. A las variables de miembro de tipo integradas se les asignan valores directamente, mientras que a los miembros de tipo personalizado se les asignan valores llamando a su operador de asignación . Por ejemplo:

class Time
{
public:
	Time()
	{
		_hour = 12;
		_minute = 0;
		_second = 0;
	}

	Time& operator=(const Time& t)
	{
		if (this != &t)
		{
			_hour = t._hour;
			_minute = t._minute;
			_second = t._second;
		}

		return *this;
	}

private:
	int _hour;
	int _minute;
	int _second;
};

class DateTime
{
public:
	DateTime(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
	// 自定义类型成员
	Time _t;
};

int main()
{
	DateTime d1;
	DateTime d2;

	d1 = d2;// 调用默认的赋值运算符函数
	// 内置类型直接赋值
	// d1._year = d2._year;
	// d1._month = d2._month;
	// d1._day = d2._day;

	// d1._t = d2._t; // 调用Time类型的赋值运算符函数
	return 0;
}

Para la clase de fecha, podemos usar la función de operador de asignación predeterminada. Cuando la clase involucra administración de recursos, necesitamos implementar el operador de asignación de la clase nosotros mismos. Por ejemplo, la clase de pila puede hacer referencia a su constructor de copia para implementar la sobrecarga del operador de asignación. .

Obtener recarga de dirección

        La sobrecarga de direcciones y la sobrecarga de direcciones const ( consulte las funciones miembro const a continuación) son la función miembro predeterminada de la clase. Generalmente, no necesitamos definirla y la genera el compilador de forma predeterminada.

// 日期类的默认取地址重载
Date* operator&()
{
	return this;
}

const Date* operator&() const
{
	return this;
}

Demostración de la sobrecarga de otros operadores.

Primero defina una función GetMonthDay para facilitar el transporte de fechas:

int Date::GetMonthDay(int year, int month)
{
	static int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

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

	return MonthDay[month];
}

Sobrecarga +-, +=, -=

// 日期+天数
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)
{
	if (day < 0)
		return *this += (-day);

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

		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

Date Date::operator-(int day)
{
	Date tmp(*this);
	tmp -= day; // 复用-=

	return tmp;
}

Prefijo++,--

// 日期+1天
Date& Date::operator++()
{
	*this += 1;  // 复用+=
	return *this;// 返回++后的日期
}
// 日期-1天
Date& Date::operator--()
{
	*this -= 1;  // 复用-=
	return *this;// 返回--后的日期
}

Sufijo++,--

Para distinguirlo del prefijo ++ y --, C++ estipula que el postfijo ++ y -- debe agregar un parámetro formal int. Cuando se llama explícitamente al sufijo ++, se debe pasar un valor int (normalmente 0).

Date Date::operator++(int)
{
	Date tmp(*this);

	*this += 1;
	
	return tmp; // 返回++前的值
}

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

	*this -= 1;
 
	return tmp; // 返回--前的值
}

Date d1;
d1++;            // 后置++
d1.operator++(0);// 显式调用

Comparación: >, <, >=, <=, ==, !=

bool Date::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 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); // 复用==
}

Operador de salida sobrecargado<<

        La biblioteca estándar IO usa >> y << para realizar operaciones de entrada y salida. La biblioteca ha sobrecargado la versión de tipo incorporada como se muestra a continuación. Podemos sobrecargar >> y << de la clase para implementar la entrada y salida de la clase. objetos.

        El primer parámetro formal del operador de salida es una referencia al objeto no constante de ostream. El segundo parámetro formal suele ser una referencia constante, porque la impresión del objeto generalmente no cambia su contenido. Para admitir una salida continua, el tipo de retorno es un parámetro formal de ostream.

Por ejemplo, sobrecargar << para la clase de fecha:

// 在类中声明友元函数	
friend ostream& operator<<(ostream& out, const Date& d);

// 重载日期类的全局<<
ostream& operator<<(ostream& cout, const Date& d)
{
	cout << d._year << "年" << d._month << "月" << d._day << "日";

	return out;
}

En este momento, no es necesario que exista la función miembro Imprimir.

Los operadores de entrada y salida deben ser funciones que no sean miembros . Si los operadores de entrada y salida son funciones miembro de una clase, entonces los objetos en el lado izquierdo de ellos solo pueden ser objetos de la clase. Esta forma no es compatible con la biblioteca estándar.

Con fecha de;

d << cout; // Si operador<< es una función miembro de la clase

Operadores de entrada sobrecargados >>

        El primer parámetro del operador de entrada es la introducción de la secuencia (objeto iostream, etc.) que se va a leer, y el segundo parámetro es la referencia al objeto (no constante) que se va a leer. El valor de retorno suele ser una referencia a una secuencia.

Por ejemplo, sobrecargando la clase de fecha >>:

// 类中声明友元函数
friend istream& operator>>(istream& in, Date& d);

// 重载日期类的全局>>
istream& operator>>(istream& cin, Date& d)
{
	in >> d._year >> d._month >> d._day;

	return in;
}

Prueba de entrada y salida:

int main()
{
	Date d1(2023,1,1);

	cout << d1 << endl;

	cout << "输入日期:";
	cin >> d1;

	cout << d1 << endl;

	return 0;
}

resultado de la operación:

sobrecarga[ ]

Al implementar una tabla de secuencia en lenguaje C, la impresión de los elementos de la tabla de secuencia solo se puede lograr definiendo una función Imprimir. Ahora simplemente diseñe una tabla de secuencia y sobrecargue el operador [] para que el acceso a los datos de la tabla de secuencia sea tan conveniente e intuitivo como una matriz.

#include<assert.h>
// 顺序表
struct SeqList
{
public:
	void PushBack(const int& x)
	{
		// ... 扩容
		_a[_size++] = x;
	}

	size_t size() 
	{
		return _size;
	}

	// 读/写
	int& operator[](size_t i)
	{
		assert(i < _size);

		return _a[i];
	}

private:
	// C++11
	int* _a = (int*)malloc(sizeof(int) * 10);
	size_t _size = 0;
	size_t _capacity = 0;
};

int main()
{
	SeqList sl;
	sl.PushBack(1);
	sl.PushBack(2);
	sl.PushBack(3);
	sl.PushBack(4);

	for (size_t i = 0; i < sl.size(); i++)
	{
		sl[i] *= 2; // 修改
		cout << sl[i] << " "; // 读取
	}

	return 0;
}

resultado de la operación:

2 funciones miembro constantes

        Una función miembro de clase modificada constantemente se llama función miembro constante. La función miembro de clase modificada constantemente en realidad modifica este puntero implícito en la función miembro de clase , lo que indica que el objeto señalado por este puntero no se puede modificar en la función miembro.

Por ejemplo, para la función Imprimir de la clase de fecha, necesitamos sobrecargar una versión modificada constantemente para imprimir objetos modificados constantemente.

// 非const修饰
void Date::Print()
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
// const修饰版本
void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

Date d1;
const Date d2(2023,9,10);
d1.Print(); // 调用Print()
d2.Print(); // 调用Print() const

De hecho, los parámetros formales constantes pueden recibir parámetros reales no constantes, por lo que la función Imprimir de la clase de fecha solo necesita conservar la versión modificada constante.

Para las tablas de secuencia, el valor de retorno del operador modificado const[] también debe modificarse con const para lograr miembros de solo lectura de objetos const.

// 只读
const int& SeqList::operator[](size_t i) const
{
	assert(i < _size);

	return _a[i];
}

// 读/写
int& SeqList::operator[](size_t i)
{
	assert(i < _size);

	return _a[i];
}

La modificación de costos implica principalmente la comprensión de los permisos. Piense en las siguientes preguntas:

1. ¿Puede un objeto constante llamar a funciones miembro no constantes? No
2. ¿Pueden los objetos no constantes llamar a funciones miembro constantes? ¿Se puede
3. ¿Se pueden llamar a otras funciones miembro no constantes dentro de una función miembro constante? No
4. ¿Se pueden llamar a otras funciones miembro de costo dentro de una función miembro que no es de costo? Poder

explicar:

1,3 Una función miembro/objeto constante llama a una función miembro no constante, es decir, pasa el parámetro real modificado constantemente (solo lectura) a un parámetro formal no constante (lectura-escritura), que es una amplificación de permisos y no se puede llamar.

2,4 Una función miembro/objeto constante llama a una función miembro constante, es decir, pasa parámetros reales modificados no constantes (lectura y escritura) a un parámetro formal constante (solo lectura), lo cual es una reducción de permisos y puede ser llamado.

3 Clase de fecha de implementación

        En la explicación anterior, se han implementado la mayoría de las funciones de la clase de fecha, a continuación se proporciona un código más completo de la clase de fecha, que principalmente agrega función de operador (fecha-fecha) y modifica las funciones miembro requeridas con const. Puede consultar los ejercicios de código.

Utilice una compilación separada, declare la clase de fecha en el archivo de encabezado Date.h y defina las funciones miembro de la clase en el archivo Date.cpp.

Archivo fecha.h

#include<iostream>
using namespace std;

class Date
{
    // 友元声明
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

public:

	// 全缺省的构造函数
	Date(int year = 1970, int month = 1, int day = 1);

	// 拷贝构造函数
	Date(const Date& d);

	// 获取某年某月的天数
	int GetMonthDay(int year, int month);

	// 赋值运算符重载
	Date& operator=(const Date& d);

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

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

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

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


	// 前置++
	// d++ -> d.operator()
	Date& operator++();

	// 后置++
	// d++ -> d.operator(0)
	Date operator++(int);

	// 前置--
	Date& operator--();

	// 后置--
	Date operator--(int);


	// >运算符重载
	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;

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

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

Archivo fecha.cpp

#include"Date.h"

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日";

	return out;
}

istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;

	return in;
}

int Date::GetMonthDay(int year, int month)
{
	static int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

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

	return MonthDay[month];
}

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

	// 检查日期是否合法
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))
	{
		cout << "非法日期" << endl;
	}
}

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

Date& Date::operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
	return *this;
}

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) const
{
	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);
	}

	return *this;
}

Date Date::operator-(int day) const
{
	Date tmp(*this);
	tmp -= day;

	return tmp;
}

Date& Date::operator++()
{
	*this += 1;
	return *this;
}

Date Date::operator++(int)
{
	Date tmp(*this);

	*this += 1;
	
	return tmp;
}

Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

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

	*this -= 1;

	return tmp;
}

bool Date::operator > (const Date& d) const
{
	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 Date::operator == (const Date& d) const
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}

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

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

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

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

int Date::operator-(const Date& d) const
{
	Date max = *this;
	Date min = d;
	int flag = 1;

	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}

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

	return n * flag;
}

Si el contenido de este artículo es útil para usted, puede darle me gusta y guardarlo. Gracias por su apoyo y esperamos su atención.

Vista previa del siguiente artículo: clases y objetos C ++ (4) lista de inicialización, miembros estáticos, amigos, clases internas

Supongo que te gusta

Origin blog.csdn.net/2301_79391308/article/details/132579955
Recomendado
Clasificación