C++语言笔记系列之十七——虚基类

1.虚基类

考虑这样一种情况:当某个类的部分或者全部直接基类是另一个共同基类派生而来,这些直接基类从上一级基类继承而来的成员就一定拥有相同的名称,这样就会产生二义性问题。

解决办法:当派生类和直接基类产生了二义性问题-->加类的作用域。

当派生类和间接基类产生了二义性问题-->虚基类。

2.虚基类的说明:

class 派生类名:virtual 访问权限 基类名

{派生类定义};

注意:在定义派生类时将需要继承的基类进行虚化声明,虚基类的说明在派生类的定义中完成。

作用:将基类说明为虚基类之后,无论虚基类产生多少个派生类都不产生基类副本——多个派生类共用一个基类。

注意:若一个基类派生出多个派生类,必须在每个派生类的定义中将基类声明为虚基类,任何一个派生类未进行声明,那么系统就会为该派生类产生基类副本。

example 1

class One

{int b;};

class Baseone:virtual public One

{int b1;};

class Basetwo:virtual public One

{int b2;};

class Basethree:public Baseone, public Basetwo

{};

3.虚基类的初始化

(1)派生类构造函数的调用顺序:基类构造-->子对象构造-->派生类构造。

(2)虚基类的构造在非虚基类构造之前完成,若同一层有两个虚基类,按说明顺序调用构造。(3)虚基类只构造一次。

(4)若虚基类是非虚基类派生类,则先调用非虚基类构造。

(5)析构顺序严格与构造相反。

example 2

#include <iostrem.h>

class Base1

{

public:

Base1()

{

cout<<"Base1 construction."<<endl;

}

};

class Base2

{

public:

Base2()

{

cout<<"Base2 construction."<<endl;

}

};

class Level1:public Base2, virtual public Base1

{

public:

Level1() {cout<<"Level1 construction."<<endl;}

};

class Level2:public Base2, virtual public Base1

{

public:

Level2() {cout<<"Levle2 construction."<<endl;}

};

class Toplevel:public Level1, virtual public Level2

{

public:

Toplevel() {cout<<"Toplevel construction."<<endl;}

};

int main()

{

Toplevel obj;

}

程序输出:

Base1 construction.

Base2 construction.

Levle2 construction.

Base2 construction.

Level1 construction.

Toplevel construction.

分析:obj是Toplevel的对象,Toplevel有一个虚基类和一个非虚基类,按照规则,先构造虚基类,即Level2。而Level2又有一个虚基类和非虚基类,同样先构造虚基类,即Base1,Base1构造之后会构造Level2的非虚基类Base2,最后才构造Level2。至此,Toplevel的虚基类构造完成,下面该构造TopLevel的非虚基类Level1,首先构造器虚基类Base1,因为Base1已经构造过,所以不再构造,下面是Base2,最后是Level1。最后一步就是

构造Toplevel了。

example 3

#include <iostrem.h>

class A

{

public:

A(char i) {cout<<"A construction "<<i<<endl;}

~A() {cout<<"A destruction."<<endl;}

};

class B:virtual public A

{

public:

B(char i, char j):A(i)//由于虚基类A属于有参构造,所以必须在派生类B中调用A的构造

{

cout<<"B construction "<<j<<endl;

}

~B() {cout<<"B destruction."<<endl;}

private:

char b;

};

class C:virtual public A

{

public:

C(char i, char j):A(i)//尽管派生类C并没有调用虚基类A的构造函数,因为在派生类B中已经

//调用过,但C的构造函数中必须要声明对虚基类A的有参调用。

{

cout<<"C construction "<<j<<endl;

}

};

class D:public B, public C

{

public:

D(char i, char j, char k, char l, char m, char n):

C(k,l), B(i,j), A(n), aa(m)

{

cout<<"D construction."<<endl;

}

private:

A aa;

};

int main()

{

D obj(‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘);

}

程序输出:

A construction f

B construction b

C construction d

A construction e

D construction.

A destruction.

B destruction.

A destruction.

注意:在D的构造函数中有A(i),这个位置加上A(i)是必须的。A的构造用的是f。

分析:在D构造中加A(i)是必须的,因为A的构造会在B和C的构造之前,也就是说传给B和C的参数是不能用来构造A的,所以A的有参构造必须来自于D,并且取的是D传递给A的参数值。

4.调用顺序

(1)所有虚基类构造函数按照它们被继承的顺序构造。

(2)所有非虚基类的构造函数按照它们被继承的顺序构造。

(3)所有子对象按照它们的声明顺序构造。

(4)派生类自己的构造:

A.在一个初始化列表中同时出现虚基类和非虚基类成员函数的调用,虚基类优先于非虚基类。

B.同一层的虚基类按照说明顺序调用构造,非虚基类按照继承顺序调用构造。

C.所有的虚基类在继承中只构造一次,虚基类的每个子对象都要调用一次构造。

5.虚基类的赋值兼容规则

(1)派生类地址可以赋值给虚基类指针。

(2)虚基类引用可以引用派生类对象。(都是大的可以赋值给小的)

C++语言笔记系列之十七——虚基类,布布扣,bubuko.com

时间: 2024-08-02 06:42:00

C++语言笔记系列之十七——虚基类的相关文章

C++语言笔记系列之十五——派生类、基类、子对象的构造和析构函数调用关系

例子 example 1 注:若一个基类同时派生出两个派生类,即两个派生类从同一个基类继承,那么系统将为每一个简历副本,每个派生类独立地使用自己的基类副本(比如基类中有属于自己类的静态变量等). #include <iostream.h> class Person { public: person() {cout<<"Construction of person."<<endl;} ~person() {cout<<"Destr

C++语言笔记系列之十八——虚函数(1)

1.C++中的多态 (1)多态性:同一个函数的调用可以进行不同的操作,函数重载是实现多态的一种手段. (2)联编:在编译阶段进行联接,即是在编译阶段将一个函数的调用点和函数的定义点联接起来. A.静态联编:在编译阶段就完成的函数联编--函数重载. B.动态联编:在程序的运行阶段由系统自动选择具体的函数--虚函数. 注:C++的多态主要指的就是动态联编. 2.虚函数 (1)虚函数是在函数的定义时将其声明为虚函数即可. (2)说明:virtual 数据类型 函数名(参数表) {函数体} A.目的:当

C++语言笔记系列之十九——虚函数(2)

1.构造函数和析构函数中调用虚函数 (1)采用静态编译. (2)在编译阶段自动联接自己类中的函数或基类中的函数,不在派生类中重定义一个函数. 注:构造函数和析构函数中调用的虚函数,派生类都不一定存在的情况下,只能去调用基类或者自身的函数. example 1 #include <iostream.h> class A { public: A() {} virtual void func() {cout<<"A construction."<<endl

C++语言笔记系列之十二——C++的继承

C++的继承 1.继承方式 public(公有继承) 派生类中的成员可以访问基类的public成员和protected成员,但不能访问基类的private成员. 派生类的对象只能访问基类的public成员. protected(保护继承),private(私有继承) 派生类中的成员可以访问基类的public成员和protected成员,但不能访问基类的private成员. 派生类的对象不能访问基类的任何成员. 2.例子 example 1: #include <iostream.h> clas

C++语言笔记系列之十六——赋值兼容规则&amp;多继承的二义性

1.赋值兼容规则 (1)派生类对象可以给基类对象赋值,这种情况下派生类对象将从基类继承的成员的值赋值给一个基类对象:但是不允许将一个基类的对象赋值给一个派生类. (2)可以将派生类对象的地址赋给基类指针,使基类指针指向派生类对象,通过基类指针引用成员时只可以引用派生类从基类继承而来的成员,而不允许引用派生类的新成员. (3)引用与指针相同. 2.多继承 (1)一个派生类从两个以上的基类中去继承. (2)说明: class 派生类名:继承方式 基类1, 继承方式 基类2,...... {派生类成员

C++语言笔记系列之十三——派生类构造函数的调用

1.派生类构造函数的调用 (1)一个基类的所有数据成员均被派生类继承.创建一个派生类对象时,系统在为派生类对象分配单元时一定要为其基类数据成员分配子空间. (2)一个派生类对象在创建时不仅要调用派生类构造函数,而且要调用基类构造函数. 派生类中的数据成员在派生类中构造. 基类中的数据成员在基类中构造. 原因: A.构造函数不继承. B.派生类的构造函数必须通过调用基类的构造函数完成基类数据成员的初始化. C.若派生类中含有子对象,必须调用子对象的构造函数. 2.派生类的构造函数 派生类名(总参数

C++语言笔记系列之十四——继承后的访问权限

1.析构函数不继承:派生类对象在析构时,基类析构函数的调用顺序与构造函数相反. 注:派生类对象建立时要调用基类构造函数,派生类对象删除时要调用基类析构,顺序与构造函数严格相反. 2.例子 example 1 #include <iostream.h> #include <math.h> class Point { public: Point(double a, double b, doule c) { x = a; y = b; z = c; } double Getx() {re

C++ Primer学习笔记32_面向对象编程(3)--继承(三):多重继承、虚继承与虚基类

C++ Primer学习笔记32_面向对象编程(3)--继承(三):多重继承.虚继承与虚基类 一.多重继承 在C++语言中,一个派生类可以从一个基类派生,称为单继承:也可以从多个基类派生,称为多继承. 多重继承--一个派生类可以有多个基类 class <派生类名> : <继承方式1> <基类名1>,<继承方式2> <基类名2>,... { <派生类新定义成员> }; 可见,多继承与单继承的区别从定义格式上看,主要是多继承的基类多于一个

C++语言笔记系列之十——静态成员

1.静态成员 (1)由关键字static修饰 静态变量定义语句在编译阶段就执行,运行过程中不再执行. (2)分类:静态数据成员.静态成员函数. (3)静态成员时类的所有对象共享的成员,而不是某一个对象的成员. 2.静态成员的使用 (1)在定义说明前加上static关键字.例如: static int x: (2)静态数据成员必须进行初始化,并且初始化必须在类外完成. (3)静态数据成员的初始化 数据类型 类名::静态数据成员名 = 值://注意这里没有static出现 (4)绝对不能使用对象名来