Compartilhamento de tecnologia

Lista de inicialização de construtor, membros estáticos, amigos, classes internas [Classes e Objetos (Parte 2)]

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

PS: O código a seguir foi testado no ambiente VS2022, o que não significa que todos os compiladores possam passá-lo.
PS: Nenhum dos códigos de teste mostra a declaração do arquivo de cabeçalho stdio.h. Adicione-o você mesmo ao usá-lo.

  

Insira a descrição da imagem aqui

                                           Página inicial do Blogger: LiUEEEEE
                                                Coluna C++
                                              Coluna da linguagem C
                                            Coluna de estrutura de dados
                                            Coluna do algoritmo de classificação
                                         Coluna sobre temas clássicos de Niuke

1. Lista de inicialização do construtor

1.1 Olhando para trás, para o construtor


No meu blog anterior

          Construção, destruição, cópia [Classes e Objetos (Parte 2)]
O construtor da classe é mencionado em. Tomando a classe de data como exemplo, o método de inicialização é o seguinte:

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Conforme mostrado no código acima, sem um estudo mais aprofundado de C++, os usuários geralmente pensarão que o código no construtor é a inicialização de variáveis ​​​​de membros da classe, mas o fato é exatamente o oposto. Em C++, a instrução principal do construtor apenas inicializa. as variáveis-membro não podem ser chamadas de inicialização no verdadeiro sentido, porque a inicialização só pode ser inicializada uma vez, enquanto o construtor pode atribuir valores às variáveis-membro várias vezes, por exemplo:

	Date(int year, int month, int day)
	{
		_year = year;
		_year += 1;
	}
  • 1
  • 2
  • 3
  • 4
  • 5

1.2 Lista de inicialização


Neste momento, a lista de inicialização em C++ é introduzida.
A lista de inicialização é uma lista de membros de dados começando com ":" (dois pontos) e separados por "," (vírgula). Cada variável de membro é seguida por um colchete, e o valor colocado entre colchetes é o valor inicializado, por exemplo. :
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
  • 1
  • 2
  • 3
  • 4
  • 5

você precisa estar ciente é:

  1. Cada variável de membro só pode aparecer uma vez na lista de inicialização (a inicialização só pode ser inicializada uma vez)
  2. A classe contém os seguintes membros, que devem ser colocados na lista de inicialização para inicialização:
    • Variáveis ​​de membro de referência
    • Variáveis ​​de membro de tipo personalizado (e o tipo personalizado não possui um construtor padrão: um construtor sem parâmetros, um construtor totalmente padrão, um construtor gerado automaticamente pelo sistema)
    • variáveis ​​​​de membro modificadas const

As variáveis ​​de membro customizadas mencionadas acima (sem construtor padrão) significam que o construtor da variável de membro customizada definida é um construtor com parâmetros, e os parâmetros precisam ser passados ​​antes que possam ser inicializados quando o usuário implementa explicitamente o construtor, se o usuário. a lista de inicialização não for escrita, o compilador irá gerá-la automaticamente para o usuário. No entanto, se a inicialização de variáveis ​​​​de membros personalizados exigir a participação de parâmetros, o sistema não passará os parâmetros por padrão, o que resultará na não inicialização. são para evitar que ocorra uma situação de não inicialização para evitar problemas desnecessários no uso subsequente.

1.3 Dicas sobre lista de inicialização


Ao escrever a lista de inicialização, a ordem de inicialização das variáveis-membro precisa ser consistente com a ordem de declaração na classe, pois o compilador inicializa as variáveis ​​​​em sequência de acordo com a ordem de declaração. Se o usuário não escrever na ordem, isso causará. problemas desnecessários, como:
class A
{
public:
	A(int n)
		:_a2(n)
		,_a1(_a2)
	{}
	void Print()
	{
		cout << "_a1 = " << _a1 << endl;
		cout << "_a2 = " << _a2 << endl;
	}
private:
	int _a1;
	int _a2;
};

int main()
{
	A aa(100);
	aa.Print();

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Insira a descrição da imagem aqui
Quando inicializado conforme mostrado na figura acima, serão alcançados resultados inconsistentes com as expectativas, portanto, tais operações devem ser evitadas ao utilizá-lo.



2. palavra-chave explícita


Para o construtor, existe também uma função dedicada aConstrutor de parâmetro únicoouConstrutor com vários parâmetros, mas sem valor padrão para o primeiro parâmetroConversão de tipo implícita, por exemplo:
class Date
{
public:
	Date(int year)
		: _year(year)
	{}
	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;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
class Date
{
public:
	Date(int year, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(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;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

Ao atribuir passando um valor, por exemplo:

	Date date(2024);
  • 1

No nível do compilador, 2024 não é atribuído diretamente ao objeto de classe criado, mas 2024 é convertido em um objeto de classe temporário por meio de conversão implícita e, em seguida, atribuído ao objeto de classe que precisa ser inicializado por meio de tal objeto. é necessário O objeto de classe inicializado é A e o objeto de classe temporário é B:

	Date A(2024);
	等价于
	Date A = B//B的值为2024
  • 1
  • 2
  • 3

Para evitar a conversão implícita, C++ criou a palavra-chave explicit. Adicionar explicit antes do construtor pode impedir que a conversão implícita aconteça, por exemplo:
	explicit Date(int year)
		: _year(year)
	{}
  • 1
  • 2
  • 3




3. membros estáticos

3.1 conceito de membro estático


Os membros da classe declarados como estáticos são chamados de membros estáticos da classe. As variáveis ​​de membro modificadas com estática são chamadas de variáveis ​​de membro estáticas modificadas com estáticas; Variáveis ​​de membro estático devem ser inicializadas fora da classe, por exemplo:
class Date
{
public:
	Date(int year, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
	static int _i;
};

int Date::_i = 1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.2 características estáticas dos membros


  1. Os membros estáticos são compartilhados por todos os objetos da classe e não pertencem a um objeto específico. Eles são armazenados na área estática.
  2. Variáveis ​​​​de membro estático devem ser definidas fora da classe. A palavra-chave estática não é adicionada durante a definição.
  3. Membros estáticos da classe podem ser acessados ​​usando class name::static member ou object.static member
  4. As funções de membro estáticas não ocultam esse ponteiro e não podem acessar nenhum membro não estático.
  5. Membros estáticos também são membros da classe e são restritos por qualificadores de acesso público, protegido e privado.




4. Vocêyuan


Os amigos fornecem uma maneira de romper o encapsulamento e, às vezes, proporcionam conveniência. No entanto, os amigos aumentarão o acoplamento e destruirão o encapsulamento, portanto, os amigos não devem ser usados ​​mais de uma vez.

Os amigos são divididos em: funções de amigos e classes de amigos

4.1 Funções de amigos


Função amiga significa que quando uma função fora da classe precisa acessar membros privados da classe, ela pode ser acessada declarando esta função na classe como uma função amiga da classe, por exemplo:
class Date
{
	friend void Print(Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
void Print(Date& d)
{
	cout << d._year << " " << d._month << " " << d._day <<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • Funções amigas podem acessar membros privados e protegidos de uma classe, mas não funções-membro da classe
  • Funções de amigos não podem ser modificadas com const
  • Funções amigas podem ser declaradas em qualquer lugar em uma definição de classe e não são restritas por qualificadores de acesso de classe.
  • Uma função pode ser uma função amiga de múltiplas classes
  • O princípio de chamar funções amigas é o mesmo das funções comuns.

4.2 Aulas de amigos


Quando uma classe deseja acessar os membros privados de outra classe, ela pode declarar essa classe como amiga na classe que precisa ser acessada, e então pode acessar seus membros privados, por exemplo:
class Time
{
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
					   //中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
			: _hour(hour)
			, _minute(minute)
			, _second(second)
	{}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
			: _year(year)
			, _month(month)
			, _day(day)
	{}	
	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • As relações de amizade são unilaterais e não comutativas. Por exemplo, nas classes Time e Date acima, se você declarar a classe Date como sua classe amiga na classe Time, poderá acessar diretamente as variáveis ​​​​de membro privado da classe Time na classe Date, mas deseja acessar o variáveis ​​de membro privado da classe Date na classe Time No.
  • A relação de amizade não pode ser transitiva Se C é amigo de B e B é amigo de A, isso não pode significar que C é amigo de A.




5. Aula interna


C++ suporta a criação de outra classe dentro de uma classe, que é chamada de classe interna. A classe interna é uma classe independente. Ela não pertence à classe externa e os membros da classe interna não podem ser acessados ​​por meio dos objetos da classe externa. . A classe externa não tem nenhum acesso superior à classe interna.
E a classe interna é naturalmente amiga da classe externa, ou seja, a classe interna pode acessar os membros privados da classe externa.

característica:

  1. As classes internas podem ser definidas como públicas, protegidas ou privadas em classes externas.
  2. Observe que as classes internas podem acessar diretamente membros estáticos nas classes externas sem exigir o nome do objeto/classe da classe externa.
  3. sizeof(classe externa)=classe externa, não tem nada a ver com classes internas.




6. Conclusão


Insira a descrição da imagem aqui

  Muito obrigado por ver meu artigo original.
  Este artigo é usado principalmente para aprendizado pessoal e compartilhamento de conhecimento. A jornada de aprendizado é longa. Se houver algum erro, obrigado por me corrigir.
  Se precisar citar, indique o endereço.