[Classes e objetos C++] Quais funções de membro padrão uma classe possui? (superior)

Índice

1. As 6 funções de membro padrão da classe

2. Construtor (*^▽^*)

2.1 Conceito

2.2 Características

3. Destruidor (*^▽^*)

3.1 Conceito

3.2 Características

4. Copie o construtor (*^▽^*)

4.1 Conceito

4.2 Características

5. Sobrecarga do operador de atribuição (*^▽^*)

5.1 Sobrecarga do operador

5.2 Sobrecarga do operador de atribuição


ヾ(๑╹◡╹)ノ" As pessoas sempre têm que pagar por sua preguiça passada!ヾ(๑╹◡╹)ノ"


1. As 6 funções de membro padrão da classe

Se não houver membros em uma classe, ela é simplesmente chamada de classe vazia.
Não há nada na classe vazia. Quando qualquer classe não escreve nada, o compilador irá gerar automaticamente as seguintes 6 funções de membro padrão.

 Função de membro padrão: A função de membro gerada pelo compilador sem implementação explícita pelo usuário é chamada de função de membro padrão.

2. Construtor (*^▽^*)

2.1 Conceito

Para a classe de dados:

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, 7, 5);//初始化
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);//初始化
	d2.Print();
	return 0;
}

Para o código acima, você pode definir a data do objeto por meio do método público Init e chamar esse método para definir as informações sempre que o objeto for criado.

O construtor é uma função de membro especial com o mesmo nome que o nome da classe , que é chamado automaticamente pelo compilador ao criar um objeto de tipo de classe para garantir que cada membro de dados tenha um valor inicial adequado e seja chamado apenas uma vez em toda a vida ciclo do objeto .

2.2 Características

O construtor é uma função de membro especial. Deve-se notar que, embora o nome do construtor seja chamado de construção, a principal tarefa do construtor não é abrir espaço para criar objetos, mas inicializar objetos .
recurso:
1. O nome da função é igual ao nome da classe.
2. Nenhum valor de retorno.
3. O compilador chama automaticamente o construtor correspondente quando o objeto é instanciado .
4. O construtor pode estar sobrecarregado. [No entanto, a maior parte do que escrevemos é o construtor padrão e geralmente não precisamos de sobrecarga de função [escreva um construtor padrão + outra sobrecarga de função]]
class Date
{
public:
	// 1.无参构造函数
	Date()//构造函数:函数名与类名相同,无返回值
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	// 2.带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestDate()
{
	Date d1; // 调用无参构造函数,注意,不能写成Date d1();
	Date d2(2015, 1, 1); // 调用带参的构造函数
	// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
}
5. Se não houver um construtor explicitamente definido na classe , o compilador C++ irá gerar automaticamente um construtor padrão sem parâmetros . Uma vez que o usuário define explicitamente o compilador, ele não irá mais gerá-lo . [Ou seja, se o construtor não for implementado, o compilador C++ gerará automaticamente um construtor padrão sem parâmetros]

 Construtor padrão: (1) construtor sem argumentos (2) construtor padrão completo (3) construtor sem argumentos gerado pelo compilador C++ [isto é, deve haver um dos três, se não houver um construtor padrão] O construtor escrito não é sem parâmetros, nem é tudo padrão】Irá relatar um erro]

6. Em relação à função de membro padrão gerada pelo compilador, se o construtor não for implementado, o compilador gerará um construtor padrão. Mas parece que o construtor padrão é inútil? O objeto chama o construtor padrão gerado pelo compilador, mas o objeto _year/_month/_day ainda é um valor aleatório [o tipo interno ainda é um valor aleatório] . Em outras palavras , o construtor padrão gerado pelo compilador é inútil aqui ?
Resposta: C++ divide os tipos em tipos internos ( tipos básicos ) e tipos personalizados. O tipo integrado é o tipo de dados fornecido pela linguagem, como: int/char..., e o tipo personalizado é o tipo que nós mesmos definimos usando class/struct/union . Quando nenhum construtor é implementado, o compilador gera um construtor padrão que chama sua função de membro padrão em membros do tipo personalizado .

Se os membros de uma classe forem todos tipos personalizados, podemos usar as funções geradas padrão. Se houver membros de função de tipo internos ou precisar exibir a inicialização do parâmetro, você mesmo deverá implementar o construtor. (você precisa passar parâmetros para inicializar, você mesmo precisa implementar o construtor) [a maioria deles escreve construtores sozinhos]

O construtor padrão gerado pelo compilador C++ não manipula as variáveis ​​de membro de função de tipo interno e o membro de tipo personalizado chamará seu próprio construtor padrão.

Nota: No C++11, foi corrigido um patch para o defeito de que membros de tipo integrado não são inicializados, ou seja, variáveis ​​de membro de tipo integrado podem receber valores padrão quando declaradas em uma classe . [Nota: Esta é uma declaração, portanto, é um valor padrão, não uma inicialização]
7. Tanto o construtor sem parâmetros quanto o construtor padrão são chamados de construtores padrão e só pode haver um construtor padrão.
Observação: Construtores sem argumentos, construtores padrão completos e construtores que não escrevemos para serem gerados pelo compilador por padrão podem ser considerados construtores padrão.
class Date
{
public:
	Date()//无参的构造函数
	{
		_year = 1900;
		_month = 1;
		_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;
};

void Test()
{
	Date d1;//这里会发生错误,默认构造函数只能有一个
}

Ocorrem erros em tempo de execução e ocorrem conflitos.

Em geral, uma classe C++ deve escrever seu próprio construtor. Existem apenas casos raros em que você pode deixar o compilador gerar automaticamente:

(1) Os membros da classe são todos membros de tipo autodefinidos e esses membros fornecem construtores padrão.

(2) O valor padrão é fornecido quando o membro de tipo interno é declarado.

3. Destruidor (*^▽^*)

3.1 Conceito

Destruidor : Ao contrário da função do construtor, o destruidor não completa a destruição do objeto , e a destruição do objeto local é feita pelo compilador. Quando o objeto for destruído, ele chamará automaticamente o destruidor para concluir algum trabalho de limpeza de recursos da classe. 【destória】

3.2 Características

Destruidores são funções de membros especiais.
recurso:
1. O nome do destruidor é o caractere ~ antes do nome da classe .
2. Sem parâmetros e sem valor de retorno.
3. Uma classe tem um e apenas um destruidor. Se não for definido explicitamente, o sistema gerará automaticamente um destruidor padrão [variáveis ​​de tipos personalizados chamarão seus próprios destruidores e variáveis ​​de tipos internos não serão processadas]. Nota: Destruidores não podem ser sobrecarregados
4. Quando o ciclo de vida do objeto termina, o sistema de compilação C++ chama automaticamente o destruidor.
5.  Em relação ao destruidor gerado automaticamente pelo compilador, o destruidor padrão gerado pelo compilador chama seu próprio destruidor para os membros de tipo personalizado.
6. Se não houver aplicativo de recurso na classe, o destruidor não pode ser gravado e o destruidor padrão gerado pelo compilador é usado diretamente, como a classe Date ; quando houver um aplicativo de recurso, ele deve ser gravado, caso contrário isso causará vazamento de recursos, como a classe Stack [Resumindo, quando houver um aplicativo de recurso, o destruidor deve ser escrito].

4. Copie o construtor (*^▽^*)

4.1 Conceito

 Quando um objeto é criado, um novo objeto é criado exatamente igual a um objeto.

Construtor de cópia : Há apenas um único parâmetro formal , que é uma referência ao objeto desse tipo de classe ( geralmente a decoração const é comumente usada ) e é chamado automaticamente pelo compilador ao criar um novo objeto com um objeto de tipo de classe existente .

4.2 Características

1. O construtor de cópia é uma forma sobrecarregada do construtor .

2. O construtor de cópia possui apenas um parâmetro e deve ser passado por referência , e o método de passagem por valor causará infinitas chamadas recursivas .
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;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

Método de passagem de valor: Chamar o construtor de cópia chamará o construtor de cópia. Para chamar esta função, você precisa primeiro passar parâmetros. A passagem de parâmetros é uma cópia temporária . Neste momento, o estado é um objeto de cópia temporária, que é equivalente a construção de cópia, e você precisa chamar a função de construção de cópia, uma chamada recursiva infinita será acionada neste momento.

Passagem de parâmetros por referência : a construção da cópia não será chamada.

[Um objeto inicializa outro objeto desta classe, que é uma construção de cópia] [Copiar um objeto é uma construção de cópia]

Para objetos de tipo personalizado, a inicialização da cópia deve ser concluída chamando o construtor de cópia.

Se nenhuma definição for exibida, o sistema gera um construtor de cópia padrão. O objeto construtor de cópia padrão é copiado em ordem de byte de acordo com o armazenamento de memória.Esse tipo de cópia é chamado de cópia rasa ou cópia de valor. [Ou seja, os membros do tipo interno completarão a cópia do valor (cópia rasa) e os membros do tipo personalizado chamarão a construção da cópia desse membro]
Conclusão: Para classes gerais, a estrutura de cópia gerada automaticamente é suficiente e não há necessidade de escrever a estrutura de cópia você mesmo. Mas para classes como stack, que gerenciam recursos diretamente por si mesmas, elas precisam implementar a cópia profunda por conta própria.
4.  Então o construtor de cópia padrão gerado pelo compilador já pode completar a cópia do valor de byte order , ainda precisamos implementá-lo nós mesmos? É claro que classes como a classe Date são desnecessárias. Classes como pilhas não são permitidas. [Há um endereço na pilha, então as duas pilhas irão para o mesmo espaço e afetarão uma à outra ao adicionar, excluir, verificar e modificar. Ao destruir, o mesmo espaço será destruído duas vezes e o mesmo espaço será lançado duas vezes. O programa entrará em colapso - a solução final, realize a construção da cópia por você mesmo - cópia profunda]
Se não houver aplicativo de recurso envolvido na classe, o construtor de cópia pode ser escrito ou não; uma vez que o aplicativo de recurso está envolvido, o construtor de cópia deve ser escrito, caso contrário, é uma cópia superficial.

 Para melhorar a eficiência do programa, ao passar parâmetros para objetos gerais, tente usar o tipo de referência o máximo possível e use referências o máximo possível de acordo com a cena real ao retornar.

5. Sobrecarga do operador de atribuição (*^▽^*)

5.1 Sobrecarga do operador

C++ introduz sobrecarga de operador para melhorar a legibilidade do código . 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. O tipo de valor de retorno e a lista de parâmetros são semelhantes às funções comuns .
O nome da função é : a palavra-chave operator seguida pelo símbolo do operador que precisa ser sobrecarregado .
Protótipo de função :  operador de operador de tipo de valor de retorno ( lista de parâmetros )
Valor de retorno: o resultado da operação
Parâmetros: operandos da operação
Perceber:
(1) Novos operadores não podem ser criados conectando outros símbolos: como operator@
(2) O operador sobrecarregado deve ter um parâmetro de tipo de classe
(3) O significado do operador usado para tipos embutidos não pode ser alterado, por exemplo: o inteiro embutido + não pode mudar seu significado
(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 da função de membro é o oculto this
(5) .*     :: sizeof ?: Observe que os 5 operadores acima não podem ser sobrecarregados. Isso geralmente surge em exames escritos.             
//日期的判断是否相等
bool operator==(const Date& d1, const Date& d2)
{
	return d1._year == d2._year 
		&& d1._month == d2._month 
		&& d1._day == d2._day;
 }
//放到类里面 Date,此时是在类外面的写法
//1.
if (operator==(d1, d2))
{
	cout << "==" << endl;
}
//2/
if (d1 == d2)
{
	cout << "==" << endl;
}
//1.和2.是等价的,编译器会处理成1.

(1) Os tipos integrados podem usar diretamente vários operadores, mas os tipos personalizados não podem usar diretamente vários algoritmos. Para permitir que vários operadores sejam usados ​​por tipos personalizados, há sobrecarga de operador.

(2) Existem tantos parâmetros de função quantos operandos. (== dois operandos; ++ um operando;)

Escrito na aula :

//日期的判断是否相等
bool operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
//放到类里面 Date,此时是在类里面的写法
//1.
if (d1.operator==(d2))
{
	cout << "==" << endl;
}
//2/
if (d1 == d2)
{
	cout << "==" << endl;
}
//1.和2.是等价的,编译器会处理成对应重载运算法调用

Se ambos os códigos existirem (o compilador pode passar porque está em conformidade com a sobrecarga de função), o compilador usará a sobrecarga de operador na classe primeiro.

//判断日期小
bool operator<(const Date& d)
{
	//小的情况
	if (_year < d._year
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day))
	{
		return true;
	}
	else
	{
		return false;
	}
}

Atenção aos detalhes aqui, não deve ser dado como certo.

5.2 Sobrecarga do operador de atribuição

1. Tipo de parâmetro
2. valor de retorno
3. Verifique se você atribui um valor a si mesmo
4. retornar *este
5. Se uma classe não definir explicitamente uma sobrecarga de operador de atribuição, o compilador também gerará uma para concluir a cópia do valor ordenado por byte do objeto
1. Formato de sobrecarga do operador de atribuição
Tipo de parâmetro : const T& , passagem por referência pode melhorar a eficiência da passagem de parâmetro
Tipo de valor de retorno : T& , referência de retorno pode melhorar a eficiência do retorno, e o objetivo do valor de retorno é dar suporte à atribuição contínua
Verifique se você atribui um valor a si mesmo
Return *this : para compor o significado de atribuição contínua
//d2 = d1;-> d2.operator(&d2, d1)
Date& operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}
int main()
{

	Date d1(2000, 8, 20);
	Date d2(2000, 9, 17);
	Date d3(d1);//拷贝构造,一个存在的对象去初始化另一个要创建的对象
	d2 = d1;//赋值重载(复制拷贝) 两个已经存在的对象之间赋值
}
2. O operador de atribuição só pode ser sobrecarregado como uma função de membro de uma classe e não pode ser sobrecarregado como uma função global
O operador de atribuição é sobrecarregado em uma função global. Observe que não há esse ponteiro ao sobrecarregar em uma função global. Dois parâmetros são necessários, mas
Se o operador de atribuição não for implementado explicitamente, o compilador gerará um padrão. Neste momento, o usuário implementa um global
A sobrecarga do operador de atribuição entra em conflito com a sobrecarga do operador de atribuição padrão gerada pelo compilador na classe, portanto, a sobrecarga do operador de atribuição só pode ser uma função de membro da classe .
3. Quando o usuário não implementar explicitamente, o compilador gerará uma sobrecarga de operador de atribuição padrão, que é copiada byte por byte na forma de value . Observação: as variáveis ​​de membro de tipo interno são atribuídas diretamente, enquanto as variáveis ​​de membro de tipo personalizado precisam chamar a sobrecarga do operador de atribuição da classe correspondente para concluir a atribuição.
4. A função de sobrecarga de atribuição padrão gerada pelo compilador já pode copiar o valor de byte order , ainda precisamos implementá-la nós mesmos? É claro que classes como a classe Date são desnecessárias. Se o gerenciamento de recursos não estiver envolvido na classe, não importa se o operador de atribuição está implementado; uma vez que o gerenciamento de recursos esteja envolvido, ele deve ser implementado.

Conhecimento suplementar: O ponteiro nulo não existe? existe e um endereço vazio é um endereço existente.

Acho que você gosta

Origin blog.csdn.net/m0_57388581/article/details/132179408
Recomendado
Clasificación