Resumen de los puntos de conocimiento del Capítulo 5 (Herencia y derivación) de Programación orientada a objetos C ++ (segunda edición)

Programación orientada a objetos C++

参考书目:《C++面向对象程序设计》—— 谭浩强 《C++程序设计:思想与方法》—— 翁惠玉



1. Los conceptos de herencia y derivación

       La herencia es una propiedad de la naturaleza de importancia universal. La derivación permite que las nuevas clases tengan personalidades más coloridas y al mismo tiempo hereden características comunes. En la programación orientada a objetos, la herencia es el principal medio para realizar la reutilización del software en C++. Generalmente, se utiliza un método de clasificación jerárquica para describir la relación entre las cosas.

Insertar descripción de la imagen aquí

       Resumen de relaciones jerárquicas de clases:

  • En el diagrama de jerarquía de clases, la clase inferior es una clase especial de la clase superior.

  • La clase baja tiene automáticamente las características de la clase alta y también tiene sus propias características nuevas.

  • Cuanto más desciendes en el diagrama de jerarquía, más específicas se vuelven sus características.

  • Esta relación jerárquica de arriba hacia abajo refleja el proceso de herencia y derivación.

  • La tecnología orientada a objetos C++ también utiliza este mecanismo de herencia.

    注:1.由基类派生出新类的过程称为派生(派生类自身也可以作为基类派生出新的派生类)。
        2.继承是指派生类自动拥有基类的属性和行为特征(派生类自动拥有基类的属性和行为,并表现出自身新的属性和行为特征 )。
    
    类的继承和派生机制使程序员无须修改已有的类,只需在既有类的基础上,根据问题的实际需要,通过增加部分代码或修改少量代码而得到新的类(派生类),从而很好的解决了程序代码的重用问题。
    

       Mecanismo de herencia de C ++:
Insertar descripción de la imagen aquí
       la llamada herencia consiste en crear una nueva clase B basada en la clase A existente. La clase A se denomina clase base o clase principal, y la clase B se denomina clase derivada o subclase. Una subclase adquiere sus características existentes de su clase principal, un fenómeno llamado herencia de clase. Desde otra perspectiva, generar una subclase a partir de una clase principal se denomina derivación de clase.

       Una clase base puede derivar varias clases derivadas y cada clase derivada se puede utilizar como clase base para derivar nuevas clases derivadas. Una clase derivada que solo deriva de una clase base se llama herencia única.
       El proceso específico se muestra en la figura:
Insertar descripción de la imagen aquí

       Una clase derivada también puede derivarse de varias clases base, lo que significa que una clase derivada puede tener dos o más clases base. Una clase derivada que tiene dos o más clases base se llama herencia múltiple.
       El proceso específico se muestra en la figura:
Insertar descripción de la imagen aquí

2. Cómo declarar clases derivadas

       El formato de la declaración de clase derivada: clase nombre de clase derivada: [método de herencia] nombre de clase base {nueva declaración de miembro de clase derivada}; los
       métodos de herencia incluyen: público, privado, protegido. Si se omite, el sistema por defecto es privado.

Ejemplo: Supongamos que se ha declarado un estudiante de clase base y se crea una clase derivada estudiante1 en base a él mediante herencia única.

class Student
{
    
     
	 private :
	   	int num;
	   	string name;
	   	char sex; 
     public:
	  	void display( )
	  {
    
    	
	      cout<<"num: "<<num<<endl;
	      cout<<"name: "<<name<<endl;
	      cout<<"sex: "<<sex<<endl;
	  }
}; 
class Student1: public Student
{
    
      
     private:
	    int age;
	    string addr;
     public:
	    void display_1()
	  {
    
      
	       cout <<"age: "<<age<<endl;
	       cout <<"address: "<<addr<<endl;
	  }
};

3. Composición de clases derivadas

       Los miembros de una clase derivada incluyen miembros heredados de la clase base y miembros agregados por uno mismo. Los miembros de la clase base heredada reflejan las características comunes de las clases derivadas de la misma clase base, mientras que los miembros recién agregados reflejan la personalidad de la clase derivada.
Insertar descripción de la imagen aquí

       Varias partes del trabajo de construir una clase derivada:

  1. Recibe miembros de una clase base. La clase derivada recibe todos los miembros de la clase base excepto el constructor y el destructor. Después de derivaciones múltiples, habrá muchos datos inútiles. Por lo tanto, no busque aleatoriamente una clase existente como clase base para construir una clase derivada.
  2. Ajustar los miembros recibidos de la clase base. Por un lado, puede cambiar los atributos de acceso de los miembros de la clase base en las clases derivadas mediante herencia y, por otro lado, puede declarar un miembro con el mismo nombre que el miembro de la clase base en la clase derivada para proteger al miembro de la clase derivada. clase base con el mismo nombre. El significado de blindaje es reemplazarla con un nuevo miembro. Miembros antiguos.
  3. Agregar miembros al declarar una clase derivada refleja la extensión de las funciones de la clase base por parte de la clase derivada.
  4. Al declarar una clase derivada, también debe definir usted mismo el constructor y el destructor de la clase derivada.

4. Acceder a los atributos de los miembros de la clase derivada.

       La clase derivada contiene miembros de la clase base y miembros de la clase derivada, lo que crea problemas con la relación entre estos dos miembros y el acceso a los atributos. Esta relación está determinada por una combinación de los atributos de acceso de los miembros de la clase base y el método de herencia de la clase derivada.

       El formato general para derivar una clase a partir de una clase base:

Insertar descripción de la imagen aquí

1.Herencia pública

       Cuando el modo de herencia de una clase derivada son atributos públicos (públicos), en la clase derivada, los atributos de acceso de los miembros públicos y miembros protegidos de la clase base en la clase derivada no cambian, y los miembros de la clase derivada no pueden cambiar directamente. acceder a los miembros privados de la clase base.

Insertar descripción de la imagen aquí

class Point	//基类Point类的声明
{
    
    
    public:	//公有函数成员
	   void InitP(float xx = 0, float yy = 0) {
    
     X = xx; Y = yy; }
	   void Move(float xOff, float yOff) {
    
     X += xOff; Y += yOff; }
	   float GetX() {
    
     return X; }
	   float GetY() {
    
     return Y; }
    private:	//私有数据成员
	   float X, Y;
};
class Rectangle : public Point  //派生类声明
{
    
    
    public:	//新增公有函数成员
	   void InitR(float x, float y, float w, float h){
    
    
	    InitP(x, y); //调用基类公有成员函数
		W = w;  H = h;}
	   float GetH() {
    
     return H; }
	   float GetW() {
    
     return W; }
    private:	//新增私有数据成员
	   float W, H;
};
int main()
{
    
    
	Rectangle rect;
	rect.InitR(2, 3, 20, 10);
	//通过派生类对象访问基类公有成员
	rect.Move(3, 2);
	cout << rect.GetX() << ','
		<< rect.GetY() << ','
		<< rect.GetH() << ','
		<< rect.GetW() << endl;
	return 0;
}

Insertar descripción de la imagen aquí

2. Herencia privada

       En una clase derivada, los miembros públicos y los miembros protegidos de la clase base sirven como miembros privados de la clase derivada. Los miembros de la clase derivada pueden acceder a ellos directamente, pero los miembros de la clase derivada no pueden acceder directamente a los miembros privados de la clase base. . Después de la herencia privada, todos los miembros de la clase base se convierten en miembros privados o miembros inaccesibles en la clase derivada y no pueden derivarse más.

Insertar descripción de la imagen aquí

3. Proteger a los miembros y proteger la herencia

       Cuando el modo de herencia de una clase derivada es el atributo de herencia protegido, en la clase derivada, los miembros públicos y los miembros protegidos de la clase base son miembros protegidos de la clase derivada. Los miembros de la clase derivada pueden acceder a ellos directamente, pero los miembros de la clase derivada pueden acceder directamente a ellos. La clase derivada no puede acceder a la clase base. Miembro privado de la clase. La herencia protegida puede derivar más, mientras que la herencia privada no.

Insertar descripción de la imagen aquí
       En conclusión:

  • Independientemente del método de herencia, las funciones miembro y amigas de la clase derivada pueden acceder a los miembros públicos y protegidos de la clase base, pero no pueden acceder a los miembros privados.
  • En la herencia pública, los objetos de la clase derivada sólo pueden acceder a miembros públicos de la clase base.
  • En herencia protegida y herencia privada, los objetos de la clase derivada no pueden acceder a ningún miembro de la clase base.
    Insertar descripción de la imagen aquí

4. Acceda a los atributos durante la derivación multinivel

       La relación de derivación de varios niveles se muestra en la figura: si la clase A es una clase base, la clase B es una clase derivada de la clase A y la clase C es una clase derivada de la clase B, entonces la clase C también es una clase derivada. de clase A. La clase B es una clase derivada directamente de la clase A y la clase C es una clase derivada indirectamente de la clase A. La clase A es la clase base directa de la clase B y la clase base indirecta de la clase C.

Insertar descripción de la imagen aquí

多级派生类的访问属性:
class A //基类
{
    
    
    private:
	   int ka;
    public:
	   int ia;
	protected:
	   void fa( );  
	   int ja;
};
class B: public A       //   public方式
{
    
    
    private:
	   int mb; 
    public:
	   void fb1( );  
	protected:
	   void fb2( );   
};
class C: protected B  //   protected方式
{
    
    
    private:
	   int nc;
	public:
	   void fc1( );  
};

Insertar descripción de la imagen aquí

5. Constructores y destructores de clases derivadas.

1. Constructor de clase derivada simple

       Una clase derivada simple tiene solo una clase base y solo un nivel de derivación. Los miembros de datos de la clase derivada no contienen objetos (es decir, subobjetos) de la clase base. Al definir el constructor de una clase derivada, además de inicializar sus propios miembros de datos, también debe llamar al constructor de la clase base para inicializar los miembros de datos de la clase base.
       El formato del constructor es el siguiente: nombre de clase derivada:: nombre de clase derivada (parámetros formales requeridos por la clase base, parámetros formales requeridos por los miembros de esta clase): nombre de clase base (lista de parámetros de clase base) {declaración de asignación de inicialización para miembros de esta clase;};
       La tabla de parámetros de la clase base enumera los parámetros reales pasados ​​al constructor de la clase base, que son los parámetros en la tabla de parámetros total del constructor de la clase derivada. Utilice los parámetros formales del constructor de la clase derivada como parámetros reales del constructor de la clase base.

Constructor de clase derivada simple

class B
{
    
    
    private:
	   int b;
    public:
	   B();
	   B(int i);
	   void Print() const;
};
class C:public B
{
    
    
    private:
	   int c;
    public:
	   C();
	   C(int i, int j);
	   void Print() const;
};
B::B()
{
    
    
	b = 0; cout << "调用B的默认构造函数." << endl;
}
B::B(int i)
{
    
    
	b = i;  cout << "调用的构造函数." << endl;
}
void B::Print() const
{
    
    
	cout << b << endl;
}
C::C()
{
    
    
	c = 0;
	cout << "调用C的默认构造函数." << endl;
}
C::C(int i, int j) :B(i)
{
    
    
	c = j;
	cout << "调用C的构造函数." << endl;
}
void C::Print() const
{
    
    
	B::Print();   cout << c << endl;
}
void main()
{
    
    
	C obj(5, 6);
	obj.Print();
}

Insertar descripción de la imagen aquí

2. Constructor de una clase derivada con subobjetos

       Además de los tipos estándar o los tipos proporcionados por el sistema, como cadenas, los miembros de datos de una clase también pueden ser tipos de clase. Por ejemplo, al declarar una clase, contiene miembros de datos del tipo de clase: Student s1; la forma general de un constructor de clase derivada: nombre de clase derivada
       :: Nombre de clase derivada (lista total de parámetros): nombre de clase base (lista de parámetros real), nombre de subobjeto (lista de parámetros) {declaración de inicialización para nuevos miembros de la clase derivada;}

       Las tareas del constructor de clases derivadas incluyen:

  • Inicializar miembros de datos de la clase base

  • Inicializar miembros de datos de objetos secundarios

  • Inicializando miembros de datos de clases derivadas

    注:不能在声明派生类时对子对象初始化,系统在建立派生类对象时调用派生类构造函数对子对象进行初始化。
    

       El orden en que se ejecutan los constructores de clases derivadas es:

  1. Llame al constructor de la clase base para inicializar los miembros de datos de la clase base.

  2. Llame al constructor del subobjeto para inicializar los miembros de datos del subobjeto.

  3. Ejecute el constructor de la clase derivada e inicialice los miembros de datos de la clase derivada.

    注:编译系统在此根据参数名(而不是参数的顺序)决定各参数表中参数之间的传递关系。如有多个子对象,要逐个列出子对象及其参数表。
    
#include <iostream>
#include <string>
using namespace std;
class Student                              //声明基类
{
    
    
    public:                                  //公用部分
	   Student(int n, string nam)       //基类构造函数
	   {
    
    num = n;     name = nam;}
	   void display()
	   {
    
    cout << "学号:" << num << endl << "姓名:" << name << endl;}
    protected:                               //保护部分
	   int num;
	   string name;
};
class Student1 : public Student   //   public继承方式
{
    
    
    private:                           //  派生类的私有数据
	   Student monitor;           //  定义子对象(班长)
	   int age;    
	   string addr;
    public:
	   Student1(int n, string nam, int n1, string nam1, int a, string ad) :Student(n, nam), monitor(n1, nam1){
    
    age = a;     addr = ad;}
	   void show()
	   {
    
    
		cout << "这个学生是:" << endl;
		display();            // 输出num和name
		cout << "年龄: " << age << endl;
		cout << "地址: " << addr << endl;
	   }
	   void show_monitor()
	   {
    
    
		cout << endl << "班长是:" << endl;
		monitor.display();  //调用基类成员函数	 
	   }	
};
int main()
{
    
    
		Student1 stud1(101, "王力",110,"李军",19,"上海市北京路115号");
		stud1.show();             //  输出第一个学生的数据
		stud1.show_monitor();     //  输出子对象的数据
		return 0;
}

Insertar descripción de la imagen aquí

3. Constructor en derivación multinivel.

       Una clase puede derivar una clase derivada y las clases derivadas pueden continuar derivando, formando una jerarquía derivada.

Insertar descripción de la imagen aquí

可以按照前面派生类构造函数的规则逐层写出各个派生类的构造函数。
基类的构造函数首部:
Student(int n, string nam );
派生类Student1的构造函数首部:
Student1(int n,string nam,int a):Student(n,nam);
派生类Student2的构造函数首部:
Student2(int n,string nam,int a,int s):Student1(n,nam,a);
写派生类构造函数的规则是,只须调用其直接基类的构造函数即可,不要列出每一层派生类的构造函数。
在声明Student2类对象时,调用Student2构造函数,在执行Student2构造函数时,先调用Student1构造函数,在执行Student1构造函数时,先调用基类Student构造函数。
初始化的顺序是:
①先初始化基类的数据成员num和name
②再初始化Student1的数据成员age
③最后初始化Student2的数据成员score
class Student                              //声明基类
{
    
    
    public:
	   Student(int n, string nam)            //基类构造函数
	   {
    
    num = n;     name = nam;}
	   void display()                           //输出基类数据成员
	   {
    
    cout << "num:" << num << endl;   cout << "name:" << name << endl;}
    protected:                                //保护部分
	   int num;        
	   string name;
};
class Student1 : public Student //声明公用派生类Student1
{
    
    
    public:
	   Student1(int n, string nam, int a) :Student(n, nam){
    
    age = a;}     //在此处只对派生类新增的数据成员初始化
	   void show()   //输出num,name和age 
	   {
    
    display(); cout << "age: " << age << endl;}
    private:                                   //派生类的私有数据
	   int age;                                  //增加一个数据成员
};
class Student2 :public Student1   //声明间接公用派生类student2
{
    
    
    public:
	   Student2(int n, string nam, int a, int s) :Student1(n, nam, a)
	   {
    
    score = s;}
	   void show_all()     //  输出全部数据成员
	   {
    
    show();       cout << "score:" << score << endl;}
	private:
	   int score;   //增加一个数据成员
};
int main()
{
    
    
	Student2 stud(10010, "李明", 17, 89);
	stud.show_all();  //输出学生的全部数据
	return 0;
}

Insertar descripción de la imagen aquí

4. Forma especial de constructor de clase derivada.

class A
{
    
    
    public:
	   A() {
    
     a = 0;  cout << "A类无参构造函数被调用" << endl; }
	   A(int i) {
    
     a = i;  cout << "A类有参构造函数被调用" << endl; }
	   void print() {
    
     cout << a << ","; }
	   int b;
    private:
	   int a;
};
class B : public A
{
    
    
    public:
	   B() {
    
     b1 = b2 = 0; }
	   B(int i) {
    
     b1 = 0;  b2 = i; }
	   B(int i, int j, int k) :A(i), b1(j), b2(k) {
    
    	}
	   void print(){
    
    A::print();	cout << b1 << "," << b2 << endl;}
    private:
	   int b1, b2;
};
int main()
{
    
    
	B b1, b2(5), b3(1, 2, 3);    b1.print();	b2.print();  b3.print();
	return 0;
}

Insertar descripción de la imagen aquí

5. Destructor de clase derivada

       Cuando se deriva una clase, la clase derivada no puede heredar el destructor de la clase base. Al eliminar el objeto de la clase derivada, el destructor de la clase derivada debe llamar al destructor de la clase base.
       La secuencia del destructor es la siguiente: primero llame al destructor de la clase derivada -> el destructor del subobjeto -> el destructor de la clase base.

class B
{
    
    
    private:	  
       int b;
    public:
	   B() {
    
     b = 0;   cout << "调用B的默认构造函数." << endl; }
	   B(int i) {
    
     b = i;   cout << "调用B的构造函数." << endl; }
	   ~B() {
    
     cout << "调用B的析构函数." << endl; }
	   void Print() const {
    
     cout << b << endl; }
};
class C :public B
{
    
    
    private:  int c;
    public:
	   C() {
    
     c = 0;   cout << "调用C的默认构造函数." << endl; }
	   C(int i, int j) :B(i) {
    
     c = j; cout << "调用C的构造函数." << endl; }
	   ~C() {
    
     cout << "调用C的析构函数." << endl; }
	   void Print() const {
    
     B::Print();	cout << c << endl; }
};
void main() 
{
    
     
    C obj(5, 6);	
    obj.Print(); 
}

Insertar descripción de la imagen aquí

6. Herencia múltiple

       Derivar de una sola clase base se llama herencia única. Cuando una clase derivada tiene dos o más clases base al mismo tiempo, se denomina herencia múltiple.

Insertar descripción de la imagen aquí

1. Declarar múltiples métodos de herencia

       clase Nombre de clase derivada: modo de herencia 1 nombre de clase base 1, modo de herencia 2 nombre de clase base 2,...{declaración de miembro;}

注:每一个“继承方式”,只用于限制对紧随其后之基类的继承。若缺省,系统默认为私有继承方式。
class A
{
    
    
    public:
       void setA(int);
       void showA();
    private:
       int a;
};
class B
{
    
    
    public:
       void setB(int);
       void showB();
    private:
       int b;
};
void A::setA(int x) {
    
       a=x;  }
void B::setB(int x) {
    
       b=x;  }
class C : public A, private B
{
    
    
   public:
      void setC(int, int, int);
      void showC();
   private:       
      int c;
}; 
void C::setC(int x, int y, int z)
{
    
       
     setA(x);      setB(y);      c=z;
}  
int main()
{
    
        
     C obj;
     obj.setA(5);   
     obj.showA();
     obj.setC(6,7,9);   
     obj.showC();
     return 0;
}

2. Herencia múltiple de constructores de clases derivadas.

       Forma del constructor de la clase derivada: nombre del constructor de la clase derivada (lista total de parámetros): constructor de clase base 1 (lista de parámetros), constructor de clase base 2 (lista de parámetros), constructor de clase base 3 (lista de parámetros)... { Declaración de inicialización derivada para nuevo miembro de la clase}

注:1.各基类的排列顺序不分先后,系统调用基类构造函数的顺序就是声明派生类时基类的出现顺序。2.多继承析构函数的执行顺序与多继承方式下构造函数的执行顺序完全相反,首先对派生类新增的数据成员进行清理,再对派生类对象成员进行清理,最后才对基类继承来的成员进行清理。
class  Base1
{
    
    
	   int  x;
    public:
	   Base1(int a) {
    
     x = a;  cout << "1的构造函数!\n"; }
	   ~Base1() {
    
     cout << "1的析构函数!\n"; }
};
class  Base2 
{
    
    
	   int y;
    public:
	   Base2(int a) {
    
     y = a;  cout << "2的构造函数!\n"; }
	   ~Base2() {
    
     cout << "2的析构函数!\n"; }
};
class Derived :public Base2, public  Base1 
{
    
    
	   int z; 	Base1  b1, b2;
    public:
	   Derived(int a, int b) :
	   Base1(a), Base2(20), b1(200), b2(a + b){
    
    z = b;   cout << "派生类的构造函数!\n";}
	   ~Derived() {
    
     cout << "派生类的析构函数!\n"; }
};
void  main(void)
{
    
    
	Derived   c(100, 200);
}

Insertar descripción de la imagen aquí

3. Ambigüedades provocadas por la herencia múltiple

       El problema más común con la herencia múltiple es la ambigüedad causada por una clase derivada que hereda un miembro de la clase base con el mismo nombre.

注:1.在多重继承时,基类与派生类之间,或基类之间出现同名成员时,将出现访问时的二义性(不确定性)——采用虚函数或同名隐藏规则来解决。2.当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性——采用虚基类来解决。
class B1
{
    
    
    public:
	   int nV;
	   void fun(){
    
    cout << "Member of B1" << endl;}
};
class B2
{
    
    
    public:
	   int nV;
	   void fun(){
    
    cout << "Member of B2" << endl;}
};
class D1 : public B1, public B2
{
    
    
    public:
	   int nV;	//同名数据成员
	   void fun(){
    
    cout << "Member of D1" << endl;}
};
void main()
{
    
    
	D1 d1;
	d1.nV = 1;
	d1.fun();

	d1.B1::nV = 2;
	d1.B1::fun();

	d1.B2::nV = 3;
	d1.B2::fun();
}

Insertar descripción de la imagen aquí
       Regla de ocultación del mismo nombre: cuando hay los mismos miembros en la clase derivada y en la clase base:

  • Si no se fuerza ningún nombre, el miembro con el mismo nombre en la clase derivada se utilizará a través del objeto de clase derivada.
  • Si desea acceder a un miembro anulado de la clase base con el mismo nombre a través de un objeto de clase derivada, debe utilizar la calificación del nombre de la clase base.

4. Clase base virtual

       Si una clase derivada tiene varias clases base directas, y estas clases base directas tienen una clase base común, se retendrán en la clase derivada varios miembros con el mismo nombre de este miembro de datos de clase base común indirecta. Si no desea conservar varios miembros de la clase base común indirecta con el mismo nombre en la clase derivada, C++ proporciona un método de clase base virtual para que la clase derivada conserve solo un miembro al heredar la clase base común indirecta.

Insertar descripción de la imagen aquí
       Si se define un constructor con parámetros en una clase base virtual y no se define ningún constructor predeterminado, se requiere que la clase base virtual se inicialice a través de la tabla de inicialización del constructor en todas sus clases derivadas (directas e indirectas).

class  A
{
    
     	A (int k) {
    
     } … … 	};
class  B: virtual public A
{
    
    	B (int n ):A(n){
    
     }… … 	};
class  C: virtual public A
{
    
    	C (int n ):A(n){
    
     } … … 	};
class  D: public B,public C
{
    
    	D (int n ):A(n),B(n),C(n) {
    
     } … … 	};
class  A 
{
    
    
    public:	
	   int x;
	   A(int  a = 0) {
    
     x = a; }
};
class B :public  virtual A 
{
    
    
    public:	
	   int y;
	   B(int a = 0, int b = 0) : A(a) {
    
     y = b; }
};
class C :public virtual A 
{
    
    
    public:	
	   int z;
	   C(int a = 0, int c = 0) :A(a) {
    
     z = c; }
};
class D :public B, public C 
{
    
    
    public:	
	   int dx;
	   D(int a1, int b, int c, int d, int a2) :B(a1, b), C(a2, c){
    
    dx = d;}
};
void  main(void)
{
    
    
	D d1(10, 20, 30, 40, 50);
	cout << d1.x << endl;
	d1.x = 400;
	cout << d1.x << endl;
	cout << d1.y << endl;
}

Insertar descripción de la imagen aquí
       Las reglas para llamar al orden de los constructores de clases base virtuales:

  • Si la misma jerarquía contiene solo varias clases base virtuales, llámelas en el orden en que se declaran y luego llame al constructor de la clase derivada.
  • Si la clase base virtual se deriva de una clase base no virtual, primero se llama al constructor de la clase base no virtual y luego al constructor de la clase derivada.
  • Si el mismo nivel contiene clases base virtuales y clases base no virtuales, primero se llama al constructor de la clase base virtual, luego al constructor de la clase base no virtual y finalmente se llama al constructor de la clase derivada.

       Las reglas para llamar al orden de múltiples constructores de herencia:

  1. Llame al constructor de la clase base virtual en el orden en que se hereda la clase base virtual;
  2. Llame a los constructores de clases base no virtuales en el orden en que se heredan de las clases base no virtuales;
  3. Llame a los constructores de objetos miembro en el orden en que se declaran;
  4. Llame al propio constructor de la clase derivada.
class OBJ1
{
    
    
    public:   OBJ1() {
    
     cout << "调用OBJ1类构造函数" << endl; }
};
class OBJ2
{
    
    
    public:   OBJ2() {
    
     cout << "调用OBJ2类构造函数" << endl; }
};
class Base1
{
    
    
    public:   Base1() {
    
     cout << "调用Base1类构造函数" << endl; }
};
class Base2
{
    
    
    public:   Base2() {
    
     cout << "调用Base2类构造函数" << endl; }
};
class Base3
{
    
    
    public:   Base3() {
    
     cout << "调用Base3类构造函数" << endl; }
};
class Base4
{
    
    
    public:   Base4() {
    
     cout << "调用Base4类构造函数" << endl; }
};
class Derived :public Base1, virtual public Base2,public Base3, virtual public Base4
{
    
    
    public:
	   Derived() :Base4(), Base3(), Base2(), Base1(), obj1(), obj2(){
    
    cout << "调用派生类构造函数成功!" << endl;}
    protected:	
	   OBJ1 obj1;	
	   OBJ2 obj2;
};
int main()
{
    
    
	Derived aa;
	cout << "派生类对象 aa 构造成功,谢谢!" << endl;
	return 0;
}

Insertar descripción de la imagen aquí

7. Reglas de compatibilidad de tipos

1. Reglas de compatibilidad de asignaciones

       Un objeto de una clase derivada pública se puede utilizar como objeto de la clase base, pero está prohibido viceversa, lo que se denomina regla de compatibilidad de asignación.

       Concretamente en:

  • Los objetos de clases derivadas se pueden asignar a objetos de clase base.
  • Los objetos de clases derivadas pueden asignar o inicializar referencias a objetos de clase base.
  • Los parámetros de función son objetos de clase base o referencias a los mismos, y los parámetros reales correspondientes pueden usar objetos de clase derivados.
  • Un puntero a un objeto de clase base también puede apuntar a un objeto de clase derivada.
  • Solo los miembros heredados de la clase base se pueden utilizar a través de punteros y nombres de objetos de clase base.
如(1):
A a1; // 定义基类 A 对象 a1 
B b1; // 定义类 A 的公用派生类 B 的对象 b1 
a1=b1; // 用派生类 B 对象 b1 对基类对象 a1 赋值,在赋值时舍弃派生类自己的成员 。 
注:赋值后不能企图通过对象 a1 去访问派生类对象 b1 的成员,因为 b1 的成员与 a1 的成员是不同的。

       Reglas de compatibilidad de asignaciones:

  • Solo puede usar objetos de subclase para asignar valores a sus objetos de clase base, pero no puede usar objetos de clase base para asignar valores a sus objetos de subclase.
  • No se pueden asignar valores entre objetos de diferentes clases derivadas de la misma clase base.
如(2):
A a1; // 定义基类 A 对象 a1 
B b1; // 定义公用派生类 B 对象 b1 
A&r=a1; // 定义基类 A 对象的引用,并用 a1 初始化。
注:1.可以用子类对象初始化引用变量 r,将最后一行改为: A& r=b1;2.保留上面第 3 行 “A& r=a1;” ,而对 r 重新赋值: r=b1;
如(3):
函数fun: 
void fun(A& r)    // 形参是类 A 的对象的引用 
{
    
    cout<<r.num<<endl; }
由于子类对象与派生类对象赋值兼容,派生类对象能自动转换类型,在调用 fun 函数时可以用派生类 B 的对象 b1 作实参 : 
fun(b1); 输出类 B 的对象 b1 的基类数据成员 num 的值。
如(4):指向基类对象的指针,也可以指向派生类对象。
class Student
{
    
    
    public:
	   Student(int, string, float);
	   void display();
    private:
	   int num;
	   string name;
	   float score;
};
Student::Student(int n, string nam, float s)
{
    
    num = n;    name = nam;    score = s;}
void Student::display()
{
    
    cout << endl << "num:" << num << endl << "name:" << name << endl << "score:" << score << endl;}
class Graduate :public Student
{
    
    
    public:
	   Graduate(int, string, float, float);
	   void display();
    private:
	   float pay;// 工资 
};
Graduate::Graduate(int n, string nam, float s, float p) : Student(n, nam, s), pay(p) {
    
     }
void Graduate::display() {
    
     Student::display(); cout << "pay = "<< pay << endl; }
void main()
{
    
    
	Student stud1(1001, "Li", 87.5);
	Graduate grad1(2001, "Wang", 98.5, 563.5);
	Student *pt = &stud1;
	pt->display(); // 调用 stud1.display 函数
	pt = &grad1; // 指针指向 grad1
	pt->display(); // 调用 grad1.display 函数 
}

Insertar descripción de la imagen aquí

2. Herencia y combinación

       El uso de objetos de otra clase como miembros de datos en una clase se denomina composición de clase. La composición de clases, al igual que la herencia, es una forma importante de reutilización del software. Establecer una relación "sí" entre una clase derivada y una clase base mediante herencia. Existe una relación "allí" entre las clases de miembros y las clases compuestas establecida mediante combinación. La herencia es vertical, la composición es horizontal.

class Teacher// 教师类 
{
    
        
     public:private:	
	    int num; 	
	    string name; 	
	    char sex; 
}; 
class BirthDate
{
    
     
     public:
     private: 	
        int year; 	
        int month; 		
        int day; 
};
class Professor: public Teacher
{
    
        
     public:private:    
        BirthDate birthday;
}; 	
Professor 类通过继承,从 Teacher 类得到了 num, name,age,sex 等数据成员。
通过组合,从 BirthDate 类得到了 year,month,day 等数据成员 。

Supongo que te gusta

Origin blog.csdn.net/weixin_43312470/article/details/108045954
Recomendado
Clasificación