Índice
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
Quarto, a função de membro padrão na classe/subclasse derivada
Seis, herança e membros estáticos
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
- 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 -
Objetos de classe base não podem ser atribuídos a objetos de classe derivados.
-
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.