Uma das três principais características da orientação a objetos - herança (Parte 1)

Índice

Preâmbulo

1. O que é Cheng Cheng?

1.1 Definição de Herança

1.2 Formalidades herdadas

1.2.1 Formulários de Uso Herdados

1.2.2 Herança e qualificadores de acesso

Em segundo lugar, classe base e conversão de cópia de objeto de classe derivada

3. Âmbito da Herança

Quarto, a função de membro padrão na classe/subclasse derivada

Sexto, Cheng Cheng e You Yuan

 Seis, herança e membros estáticos

Resumir


Preâmbulo

Este artigo explica principalmente em detalhes a herança de uma das três principais características da orientação a objetos.

Aqui explicaremos principalmente o significado e várias propriedades de 継成, e levaremos todos a entender 継成

1. O que é Cheng Cheng?

1.1 Definição de Herança

Vamos dar uma olhada em um pequeno exemplo

Observando a foto acima, descobrimos que a vaca de Picasso abstraiu as características da vaca, então o búfalo, a vaca e o gado abaixo incluem basicamente todas as características da vaca da foto acima, que podem ser consideradas como herança. A vaca de Picasso pode ser considerada uma categoria principal, e o búfalo, a vaca leiteira e o cambista podem ser considerados uma subcategoria.

O mecanismo de herança (herança) é o método mais importante da programação orientada a objetos para tornar o código reutilizável , pois permite aos programadores expandir e adicionar funções mantendo as características da classe original, gerando assim novas classes, denominadas classes derivadas . A herança apresenta a estrutura hierárquica da programação orientada a objetos, refletindo o processo cognitivo do simples ao complexo. A reutilização à qual fomos expostos antes é a reutilização de função e a herança é a reutilização de níveis de design de classe .

Aqui podemos simplesmente escrever uma classe derivada primeiro, e todos podem experimentá-la

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;

class person
{
public:
	void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
private:
	string _name = "张三";
	int _age = 18;
};

class student :public person
{

private:
	int _stuNumber;
};


int main()
{
	student aa;
	aa.print();
	return 0;
}

Executando como acima, podemos descobrir que, após a herança, os membros da classe pai se tornarão parte da subclasse e também podemos chamar as funções de membro da classe pai por meio da subclasse, então quais recursos ela possui? Vamos dar um passo a passo e depois explicar o formato da herança.

1.2 Formalidades herdadas

1.2.1 Formulários de Uso Herdados

 Conforme mostrado na figura acima, a pessoa é chamada de classe base/pai e o aluno é chamado de subclasse/classe derivada.

1.2.2 Herança e qualificadores de acesso

 O modo de herança corresponde ao qualificador de acesso em pares e as alterações específicas são as seguintes.

 Resumir:

1. Os membros privados da classe base são invisíveis, independentemente de como são herdados na classe derivada . Invisibilidade aqui significa que os membros privados da classe base ainda são herdados no objeto da classe derivada, mas o objeto da classe derivada é gramaticalmente impedido de acessá-lo, não importando dentro ou fora da classe .
2. Os membros privados da classe base não podem ser acessados ​​na classe derivada.Se o membro da classe base não deseja ser acessado diretamente fora da classe, mas precisa ser acessível na classe derivada, ele é definido como protegido. Pode-se ver que o qualificador de membro protegido é devido à herança .
3. De fato, se resumirmos a tabela acima, veremos que os membros privados da classe base não são visíveis na subclasse. público > protegido > privado pode ser considerado como permissões, e as permissões só podem ser reduzidas, mas não ampliadas .        
4. Ao usar a palavra-chave class, o método de herança padrão é privado e, ao usar struct, o método de herança padrão é público, mas é melhor escrever o método de herança explicitamente .
5. Na prática, herança pública é geralmente usada, e herança protegida/privada raramente é usada , e o uso de herança protegida/privada não é defendido, porque membros herdados protegidos/privados só podem ser usados ​​em classes derivadas. a manutenção estendida não é forte.

class person
{
public:
	void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "张三";
private:
	int _age = 18;
};

class student :private person
{
private:
	int _stuNumber;
};


int main()
{
	student aa;
	//私有継承的话父类我们办法直接访问但是可以间接访问
	aa.print();
	return 0;
}

 Conforme mostrado na figura, se for herança privada, todas as funções e variáveis ​​de membro na classe pai não podem ser acessadas diretamente, então realmente não temos como acessá-la? Podemos acessar os membros da classe pai por meio de funções de membro na subclasse .

 Na verdade, isso é semelhante a uma família: público significa abrir a porta para estranhos, protegido significa impedir estranhos e privado significa impedir estranhos e sua própria família.

Em segundo lugar, classe base e conversão de cópia de objeto de classe derivada

  1. Objetos de classes derivadas podem ser atribuídos a objetos de classes base/ponteiros de classes base/referências de classes base . Há um termo figurativo aqui chamado fatiar
    ou cortar. Significa cortar a parte da classe pai na classe derivada e atribuí-la ao passado
  2. Objetos de classe base não podem ser atribuídos a objetos de classe derivados.

  3. O ponteiro ou referência da classe base pode ser atribuído ao ponteiro ou referência da classe derivada por meio de conversão.
    Mas deve ser seguro quando o ponteiro da classe base apontar para o objeto da classe derivada. Aqui, se a classe base for um tipo polimórfico, o dynamic_cast de RTTI (RunTime Type Information) pode ser usado para identificar e realizar uma conversão segura. (ps: Explicaremos isso depois, vamos conhecer primeiro)

    class person
    {
    public:
    	void print()
    	{
    		cout << "name:" << _name << endl;
    		cout << "age:" << _age << endl;
    	}
    protected:
    	string _name = "张三";
    	int _age = 18;
    };
    
    class student :public person
    {
    private:
    	int _stuNumber;
    };
    
    
    int main()
    {
    	student aa;
    	//1.子类对象可以赋值给基类的对象,指针,引用
    	person p = aa;
    	person* ptr = &aa;
    	person& pp = aa;
    
    	//2.基类对象不能赋值给派生类
    	//aa = p;会报错
    
    	//3.基类的指针可以通过强转赋值给子类的指针
    	ptr = &aa;
    	student* ps1 = (student*)ptr;//这种情况可以
    	//这个ptr指向的空间和aa指向的空间是相同的,所以不会越界
    
    	ptr = &p;
    	student* ps2 = (student*)ptr;//这种情况可能会存在越界访问
    	//因为ptr只有父类的内容没有子类的内容
    	return 0;
    }

     

    Então, como é realizada a conversão de atribuição entre a classe base e a classe derivada, que explicaremos mais adiante.

3. Âmbito da Herança

1. No sistema de herança, a classe base e a classe derivada têm escopos independentes .

class person
{
public:
	void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "张三";
	int _age = 18;
};

class student :public person
{
public:
	void print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
		cout << "stunumber:" << _stuNumber << endl;

	}
private:
	int _stuNumber=1111111;
};


int main()
{
	student aa;
	aa.print();
	return 0;
}

O que acontecerá quando o código acima for executado?

 Conforme mostrado na figura acima, descobrimos que quando chamamos print, embora tanto a subclasse quanto a classe pai tenham print, qual é a última coisa que chamamos print na subclasse? Na verdade, isso é chamado de esconder .

2. Existem membros com o mesmo nome na subclasse e na classe pai, e os membros da subclasse bloquearão o acesso direto da classe pai aos membros com o mesmo nome.Esta situação é chamada de ocultação ou redefinição . Nesse caso, os membros da subclasse com o mesmo nome serão acessados ​​primeiro. (Nas funções de membro da subclasse, os membros da classe base::classe base podem ser usados ​​para exibir access )
3. Deve-se observar que, se a função do membro estiver oculta, apenas o mesmo nome de função será necessário para constituir a ocultação .
4. Observe que, na prática, é melhor não definir membros com o mesmo nome no sistema de herança .

 Como mostra a figura, através do qualificador de acesso também podemos acessar os membros de mesmo nome da classe pai, mas na herança real é melhor não definir a função membro de mesmo nome, pois ela é de pouca importância. significado.

Vamos dar às ironias uma questão de múltipla escolha


class A
{
public:
 void fun()
 {
 cout << "func()" << endl;
 }
};
class B : public A
{
public:
 void fun(int i)
 {
 A::fun();
 cout << "func(int i)->" <<i<<endl;
 }
};
void Test()
{
 B b;
 b.fun(10);
};

对于上面程序的运行,下面选项那个正确?
A:A中fun和B中fun构成函数重载
B:A中fun和B中fun构成隐藏
C:程序错误
D:上面三个选项均不正确

Para esta questão, escolhemos B. Primeiro, diversão em A e diversão em B não estão no mesmo escopo, portanto, não constituem sobrecarga de função. A diversão em A tem o mesmo nome da diversão em B, então elas estão escondidas.

Quarto, a função de membro padrão na classe/subclasse derivada

6 funções de membro padrão, "padrão" significa que, se não o escrevermos, o compilador gerará automaticamente um para nós, então como
essas funções de membro são geradas na classe derivada?

1. Quando o construtor da classe derivada é chamado, essa parte dos membros da classe base deve chamar o construtor padrão da classe base para construção . Se a classe base não tiver um construtor padrão , a classe derivada precisará chamar explicitamente na seqüência de inicialização do construtor .

2. O construtor de cópia da classe derivada deve chamar o construtor de cópia da classe base para concluir a construção da cópia da classe base

3. O operator= da classe derivada deve chamar o operator= da classe base para concluir a atribuição da classe base .

4. Quando o destruidor da classe derivada é chamado, o destruidor da classe base é chamado depois que o destruidor da classe derivada é chamado , porque isso pode garantir que os membros da classe derivada sejam limpos primeiro e depois os membros de a classe base é limpa para evitar Primeiro limpe os membros da classe base e, em seguida, a situação de ponteiros selvagens aparece.

5. Estrutura de inicialização da classe derivada, primeiro chame o construtor da classe base para inicializar a parte da classe base e, em seguida, inicialize a classe derivada.

6. O objeto de classe derivada é destruído, a classe derivada é destruída primeiro e, em seguida, a classe base é destruída.

7. Como o destruidor em alguns cenários subseqüentes precisa ser reescrito, uma das condições para reescrever é que o nome da função seja o mesmo (explicaremos isso mais tarde). Em seguida, o compilador executará um processamento especial no nome do destruidor e o processará como destructor(), portanto, quando o destruidor da classe pai não adicionar virtual, o destruidor da subclasse e o destruidor da classe pai formarão um relacionamento oculto.

 Em geral, para as 6 funções de membro padrão, a classe derivada e a classe base chamam suas respectivas funções de membro padrão .

class Person
{
public:
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}

	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}

	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}

	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	Student(const char* name, int num)
		: Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}

	Student(const Student& s)
		: Person(s)
		, _num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}

	Student& operator = (const Student& s)
	{
		cout << "Student& operator= (const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator =(s);
			_num = s._num;
		}
		return *this;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};
void Test()
{
	Student s1("jack", 18);
	Student s2(s1);
	Student s3("rose", 17);
	s1 = s3;
}
int main()
{
	Test();
	return 0;
}

 Vendo isso, também devemos ser capazes de entender como a conversão de atribuição anterior entre a classe base e a classe derivada é realizada. Na verdade, é retirar a parte da classe pai na subclasse para construção de cópia para criar uma nova classe pai, porque a subclasse deve conter variáveis ​​Member da classe pai.

 

Sexto, Cheng Cheng e You Yuan

Os relacionamentos de amigos não podem ser herdados , ou seja, os amigos da classe base não podem acessar os membros privados e protegidos da subclasse

class student;
class person
{
public:
	friend void Test(const person& p, const student& s);
protected:
	string _name = "张三";
	int _age = 18;
};

class student :public person
{

protected:
	int _stuNumber=1111111;
};

void Test(const person& p,const student& s)
{
	cout << p._name << endl;
	cout << p._age << endl;
	cout << s._stuNumber << endl;
}
int main()
{
	person pp;
	student aa;

	Test(pp,aa);
	
	return 0;
}

 Seis, herança e membros estáticos

Se a classe base definir um membro estático estático , haverá apenas um desses membros em todo o sistema de herança . Não importa quantas subclasses são derivadas, há apenas uma instância de membro estático

Resumir

O conteúdo acima é todo o conteúdo deste artigo. Ele explica principalmente a definição, formato, conversão de atribuição de classes derivadas e classes base, o escopo da herança e os seis construtores padrão de subclasses. Amigos não podem herdar. E herança de membros estáticos. Espero que os caras do ferro possam ganhar alguma coisa.

No próximo artigo, falaremos sobre a falsa herança na herança e esperamos que os veteranos continuem a apoiá-la.

Acho que você gosta

Origin blog.csdn.net/zcxmjw/article/details/129865639
Recomendado
Clasificación