Classes e objetos C++ (2) - sobrecarga do operador de atribuição e sobrecarga do operador de endereço


Prefácio:

Este capítulo aprenderá como implementar a sobrecarga do operador em profundidade por meio da implementação da classe date . Este capítulo completará o estudo das 3 restantes das 6 funções de membro padrão - sobrecarga do operador de atribuição e sobrecarga do operador de obtenção de endereço .

1. Sobrecarga do operador

1. O conceito de sobrecarga do operador

C++Para melhorar a legibilidade do código, a sobrecarga de operador é introduzida . A sobrecarga de operador é uma função com um nome de função especial e também tem seu tipo de valor de retorno, nome de função e lista de parâmetros. Seu tipo de valor de retorno e lista de parâmetros são semelhantes às funções comuns.

  • Nome da função : A palavra-chave operatoré seguida pelo símbolo do operador que precisa ser sobrecarregado .
  • Protótipo da função :
  • 返回值类型 operator操作符(参数列表)
  • bool operator==(Date d1,Date d2);

deve estar ciente é:

  1. Novos operadores não podem ser criados concatenando outros símbolos : por exemplooperator@
  2. Um operador sobrecarregado deve ter um parâmetro de tipo de classe
  3. Operadores para tipos integrados, cujo significado não pode ser alterado , por exemplo: tipos inteiros integrados +, cujo significado não pode ser alterado
  4. Quando sobrecarregado como uma função de membro de classe, seus parâmetros formais parecem ser 1 a menos que o número de operandos , porque o primeiro parâmetro de uma função de membro está ocultothis
  5. .* :: sizeof ?: .Observe que os 5 operadores acima não podem ser sobrecarregados . Isso geralmente aparece em questões de múltipla escolha por escrito.

2. Implemente a classe Date

Defina uma classe de Data:

class Date
{
    
    
public:
	//构造函数
	Date(int year = 0, int month = 0, int day = 0)
	{
    
    
		//判断日期是否合法
		//GetMonthDay()获取这个月的天数
		if (month > 0 && month < 13 &&
			(day > 0 && day <= GetMonthDay(year, month)))
		{
    
    
				_year = year;
				_month = month;
				_day = day;
		}
		else
		{
    
    
			cout << "日期非法" << endl;
		}
	}
private:
	int _year;//年
	int _month;//月
	int _day;//日
};

Interface de função:

// 类里面短小函数,适合做内联的函数,直接是在类里面定义的
class Date
{
    
    
	// 友元函数声明
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

public:
	// 构造
	Date(int year = 0, int month = 0, int day = 0);
	void Print() const;
	// 当月的天数
	int GetMonthDay(int year, int month) 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);
	Date operator-(int day) const;

	// 重载日期类与日期类运算-
	int operator-(const Date& d) const;
	
	// 赋值运算符重载
	Date& operator=(const Date& d);

	// 重载日期类前置++--,后置++--
	Date& operator++();
	// int参数 仅仅是为了占位,跟前置重载区分
	Date operator++(int);
	Date& operator--();
	Date operator--(int);

	//取地址重载
	Date* operator&();
	const Date* operator&() const;

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

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

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

Ao implementar a sobrecarga do operador , uma coisa precisa de atenção especial :

A função sobrecarregada do operador binário tem dois parâmetros , o primeiro parâmetro é o operando esquerdo e o segundo parâmetro é o operando direito .

No capítulo anterior, falamos sobre as características das funções-membro.As funções-membro têm um parâmetro próprio thise o tipo é um tipo de classe . Assim, podemos omitir o primeiro parâmetro e apenas escrever o segundo parâmetro .

então:

(1) > < >= <= != sobrecarga

Implemente duas funções de sobrecarga de operador primeiro e as outras podem reutilizar os operadores já implementados.

class Date
{
    
    
public:
	//构造函数
	//...
	bool operator==(const Date& d)
	{
    
    
		return (_year == d._year) && (_month == d._month) && (_day == d._day);
	}
	bool operator<(const Date& d) 
	{
    
    
		return _year < d._year
			|| (_year == d._year && _month < d._month)
			|| (_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator<=(const Date& d) 
	{
    
    
		//函数的复用
		return *this < d || *this == d;
	}

	bool operator>(const Date& d) 
	{
    
    
		//函数的复用
		return !(*this <= d);
	}

	bool operator>=(const Date& d) 
	{
    
    
		//函数的复用
		return !(*this < d);
	}

	bool operator!=(const Date& d) 
	{
    
    
		//函数的复用
		return !(*this == d);
	}
//...
};

(2) += -= + - sobrecarga

Nota: Os operandos certos das quatro sobrecargas de operador a seguir são daydias

class Date
{
    
    
public:
	//...
	//获取当月的天数
	int GetMonthDay(int year, int month) 
	{
    
    
		assert(month > 0 && month < 13);

		int monthArray[13] = {
    
     0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		//判断是否是闰年的二月
		if (month == 2 && 
		((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0))
		{
    
    
			return 29;
		}
		else
		{
    
    
			return monthArray[month];
		}
	}
	//+= 返回自身的引用,减少拷贝
	Date& operator+=(int day)
	{
    
    
		//判断是否加了负数
		if (day < 0)
		{
    
    
			//复用
			*this -= -day;
			return *this;
		}

		_day += day;
		while (_day > GetMonthDay(_year, _month));
		{
    
    
			_day -= GetMonthDay(_year, _month);
			//进位
			_month++;
			if (_month == 13)
			{
    
    
				_year++;
				_month = 1;
			}
		}
		return *this;
	}
	//-= 返回自身的引用,减少拷贝
	Date& operator-=(int day)
	{
    
    
		//判断是否减了一个负数
		if (day < 0)
		{
    
    
			//复用
			*this += -day;
			return *this;
		}

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

			_day += GetMonthDay(_year, _month);
		}

		return *this;
	}
	Date operator+(int day) 
	{
    
    
		//拷贝构造
		//因为加不改变自身的值,所以创建临时对象
		Date tmp(*this);
		//复用
		tmp += day;
		return tmp;
	}
	Date operator-(int day)
	{
    
    
		Date tmp(*this);
		tmp -= day;
		return tmp;
	}
//...
};

(3) Sobrecarga pré-++ e pós-++

O prefixo ++e o pós-fixo ++são operadores unários . A diferença entre os dois é:

  • Preposição ++: use primeiro++ , retorne ++o seguinte número
  • Traseiro ++: use primeiro e depois ++retorne ++ao número anterior

Para fazer uma distinção ao sobrecarregar, C++ estipula:

  • ++Ao pós- sobrecarga, adicione um intparâmetro adicional do tipo , mas esse parâmetro não precisa ser passado ao chamar e o compilador o passará automaticamente.
class Date
{
    
    
public:
	//...
	//前置++
	Date& operator++()
	{
    
    
		*this += 1;
		return *this;
	}
	//后置++
	// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,
	// 故需在实现时需要先将this保存一份,然后给this + 1
	// 而temp是临时对象,因此只能以值的方式返回,不能返回引用
	Date operator++(int)
	{
    
    
		Date tmp(*this);
		*this += 1;
		return tmp;
	}
	//前置--
	Date& operator--()
	{
    
    
		*this -= 1;
		return *this;
	}
	//后置--
	Date operator--(int)
	{
    
    
		Date tmp(*this);
		*this -= 1;
		return tmp;
	}
//...
};

(4) Data-data de realização

Data + data não tem sentido, mas data - data é significativa e data - data representa o número de dias entre duas datas

class Date
{
    
    
//...
	int operator-(const Date& d)
	{
    
    
		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;
	}
	//...
}

(5) << e >> sobrecarga

Date d1(2023,5,1);
cout<<d1;
  1. <<E é um operador binário Como mencionado acima , o primeiro parâmetro do>> operador binário é o operador esquerdo e o segundo parâmetro é o operador direito.
  2. Como o primeiro parâmetro da função de membro na classe é this, o operando esquerdo se torna o objeto e o operando direito se tornacout , o que é inconsistente d1<<coutcom os C++hábitos gramaticais usuais, portanto, não podemos escrever <<e >>na função de membro da classe, mas sobrecarregá-la fora da classe
  3. Mas as funções fora da classe não podem acessar as funções privadas da classe , portanto, definimos a função sobrecarregada como uma função amiga a ser alcançada.
class Date
{
    
    
//...
	//申明友元函数
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
	//...
}
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;
}

2. Função de membro padrão - sobrecarga do operador de atribuição

Como as funções de membro padrão anteriores , como construtores e destruidores , a sobrecarga do operador de atribuição também é uma das seis funções de membro padrão da classe .

A sobrecarga do operador de atribuição tem as seguintes propriedades:

  1. Formato de sobrecarga do operador de atribuição:
    tipo de parâmetro : const &T, referência de parâmetro pode melhorar a eficiência da passagem de parâmetro
    tipo de valor de retorno : T&, referência de valor de retorno pode melhorar a eficiência de retorno , o objetivo do valor de retorno é oferecer suporte à atribuição contínua e detectar se deve atribuir continuamente a si mesmo
    Retorno *this: a definição de atribuição contínua a ser composta
  2. Os operadores de atribuição só podem ser sobrecarregados como funções de membro de classes e não podem ser sobrecarregados como funções globais ;
class Date
{
    
    
//...
	Date& operator=(const Date& d)
	{
    
    
		if (this != &d)
		{
    
    
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	//...
}

2. Função de membro padrão - sobrecarga do operador de endereço

Existem apenas duas das 6 funções de membro padrão - sobrecarga de endereço e constsobrecarga de endereço . No entanto, realmente não há necessidade de implementar essas duas funções, porque o efeito de nossa própria implementação e da implementação automática do compilador é o mesmo.

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

#. Pontos de conhecimento suplementares: membros const

constA função de membro modificada é chamada de constfunção de membro, consta função de membro de classe modificada , e o ponteiro implícito da função de membro realmente modificada thisindica que qualquer membro da classe não pode ser modificado na função de membro .

Por exemplo:

class Date
{
    
    
public:
	//...
	void print()
	{
    
    
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
}

void Test3()
{
    
    
	Date d1(2023, 4, 1);
	d1.print();
	const Date d2(2022, 3, 1);
	d2.print();
}

resultado da operação:

insira a descrição da imagem aqui
Isso ocorre porque a autoridade é ampliada : não podemos const Date &d2passar para o parâmetro formal. Date* this
A maneira correta de escrevê-lo é:

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

Parâmetros implícitos de modificação que não podem ser exibidos , portanto, adicione a modificação *thisapós a função . constIsso é adequado para funções que não modificam variáveis ​​de membro em funções de membro e consttambém é aplicável a classes não decoradas.


Este é o final deste artigo, o texto do código não é fácil, por favor, me apoie muito!

Acho que você gosta

Origin blog.csdn.net/weixin_67401157/article/details/130527438
Recomendado
Clasificación