C++类继承和派生

学习链表继承

https://bbs.csdn.net/topics/300237086

https://www.cnblogs.com/gentle-min-601/p/9556920.html

一 单继承

由基类得到派生类。

派生方式:默认为私有继承

public 公有继承

private 私有继承

protected 保护继承

 

注意!!

派生类不能继承基类的构造函数和析构函数

 

单继承示例代码

#include <iostream>
#include<string>
#include<cstring>
using namespace std;
class Student
{
private:
    int number;
    string name;
public:
    Student() { number = 0; name= ""; }
    void SetValue(int n, string s1)
    {
        number = n;
        name = s1;
    }
    void Print()
    {
        cout << "Numver:" << number << endl;
        cout << "Name:" << name << endl;
    }
};
class UGStudent :public Student
{
private:
    int age; int grade;
public:
    UGStudent() { SetValue(0, ""); age = 0; grade = 0; }
    UGStudent(int n, string s1, int a, int g)
    {
        SetValue(n,s1);
        age = a; grade = g;
    }
    void PrintExtra()
    {
        cout << "Age:" << age << endl;
        cout << "Grade:" << grade << endl;
    }
};
int main()
{
    UGStudent st1(100, "wang", 18, 1);
    st1.Print(); //调用基类的函数
    st1.PrintExtra(); //调用派生类的新定义的函数
    return 0;
};

 

 

二 单继承的访问权限控制

  公有继承的访问权限控制

  基类的public和protected成员的访问属性在派生 类中保持不变,但基类的private成员不可访问。

  派生类中的成员函数可以直接访问基类中的 public和protected成员,但不能访问基类的 private成员。

  通过派生类的对象只能访问基类的public成员。

#include <iostream>
#include<string>
#include<cstring>
using namespace std;
class Person //定义基类Person
{
public: //外部接口
    Person(const string Name, int Age, char Sex); //基类构造函数
    string GetName()
    {
        return name;
    }
    int GetAge(); //基类成员函数的声明
    char GetSex();
    void Display();
private:
    string name;
    char sex;
protected: //保护成员
    int age;
};
Person::Person(const string Name, int Age, char Sex)
//基类构造函数的实现
{
    name = Name;
    age = Age;
    sex = Sex;
}
//基类成员函数的实现
int Person::GetAge()
{
    return(age);
}
char Person::GetSex()
{
    return(sex);
}
//基类成员函数的实现
void Person::Display()
{
    //直接访问本类私有成员
    cout << "name:" << name << '\t'; cout << "age:" << age << '\t';
    cout << "sex:" << sex << endl;
}
class Student :public Person //定义公有继承的学生类
{
public: //外部接口
    Student(string pName, int Age, char Sex, string pId, float Score) :
        Person(pName, Age, Sex)
        //调用基类的构造函数初始化基类的数据成员
    {
        id=pId; //学生类的数据初始化
        score = Score;
    }
    string GetId(string pId) //派生类的新成员
    {
        return(id);
    }
    float GetScore() //派生类的新成员
    {
        return score;
    }
    void Display(); //派生类的新成员
private:
    string id;
    float score;
};
void Student::Display() //派生类的成员函数的实现
{
    cout << "id:" << id << '\t'; //直接访问本类私有成员
    cout << "age:" << age << '\t'; //访问基类的保护成员
    cout << "score:" << score << endl;
}
int main()
{
    string name;
    cout << "Enter a person′s name:";
    cin >> name;
    Person p1(name, 29, 'm'); //基类对象
    p1.Display(); //基类对象访问基类公有成员函数
    string pId;
    cout << "Enter a student′s name:";
    cin >> name;
    Student s1(name, 19, 'f', "03410101", 95); //派生类对象
    //派生类对象访问基类成员函数
    cout << "name:" << s1.GetName() << '\t';
    cout << "id:" << s1.GetId(pId) << '\t';
    cout << "age:" << s1.GetAge() << '\t';
    cout << "sex:" << s1.GetSex() << '\t';
    cout << "score:" << s1.GetScore() << endl;
    return 0;
}

 

  私有继承的访问权限控制

基类的public和protected成员都以private身份出现 在派生类中,但基类的private成员不可访问。

派生类中的成员函数可以直接访问基类中的public 和protected成员,但不能访问基类的private成员。

通过派生类的对象不能访问基类中的任何成员。

私有继承之后,基类的成员再也无法在以后的派生 类中发挥作用,出于这种原因,一般不使用私有继 承方式。

 

好处:基类原有的外部接口被派生类封闭和隐藏起来

#include <iostream>
#include<string>
#include<cstring>
using namespace std;
class Parent //定义基类Parent
{
protected:
    int x;
public:
    void setx()
    {
        x = 0;
    }
    void display()
    {
        cout << "x=" << x << endl;
    }
};
class Son :private Parent //定义派生类Son
{
public:
    void increase() //新增成员函数
    {
        x++; //有效
    }
};
int main()
{
    Son s;
    //s.setx(); //错误:不能访问基类的任何成员
    s.increase(); //有效
    //s.display(); //错误:不能访问基类的任何成员
    return 0;
}
#include <iostream>
#include<string>
#include<cstring>
using namespace std;
class Parent //定义基类Parent
{
public:
    int pubD;
protected:
    int protD;
private:
    int privD;
};
class Son :public Parent //定义公有派生类Son
{
public:
    void fn()
    {
        int x;
        x = pubD; //有效
        x = protD; //有效
        //x = privD; //错误:不能访问
    }
};
class Daughter :private Parent //定义私有派生类
{
public:
    void fn()
    {
        int y;
        y = pubD; //有效,但pubD被降为私有
        y = protD; //有效,但pubD被降为私有
        //y = privD; //错误
    }
};
int main()
{
    Parent p; //基类对象
    p.pubD = 10; //有效
    //p.protD = 10; //错误
    //p.privD = 10; //错误
    Son s; //公有派生类对象
    s.pubD = 20; //有效
    //s.protD = 20; //错误
    //s.privD = 20; //错误
    Daughter d; //私有派生类对象
    //d.pubD = 30; //错误:不能访问,pubD已降为私有
    //d.protD = 30; //错误
    //d.privD = 30; //错误
    return 0;
}

 

私有派生类中的基类对象被降低为私有?

  

  保护继承的访问权限控制

  基类的public和protected成员都以protected身份出 现在派生类中,但基类的private成员不可访问

  派生类中的成员函数可以直接访问基类中的public 和protected成员,但不能访问基类的private成员。

  通过派生类的对象不能访问基类中的任何成员。

  保护继承与私有继承的区别在于能否进一步将基类 成员传递给派生类的派生类,保护继承可以传递部 分基类成员,但私有继承不可以。

 

#include <iostream>
using namespace std;
class demo //定义基类demo
{
protected:
int j;
public:
demo( ) { j = 0; }
void add(int i) { j += i; }
void sub(int i) { j -= i ; }
void display() {cout<<"current value of j is "<<j<<endl; }
};
class child: protected demo
{ public:
void sub(int i) { j -= i ;} //有效:访问基类中的保护数据成员
};
int main( )
{
chlid object;
object.display();
object.add(10);
object.display();
object.sub(5);
object.display();
return 0;
}

 

三  protected 成员的特点与作用  

对建立其所在类对象的模块来说(水平访问时), 它与 private 成员的性质相同。

对于其派生类来说(垂直访问时),它与 public 成 员的性质相同。

既实现了数据隐藏,又方便继承,实现代码重用。

 

比如这样,访问就是错误的

// 例 protected 成员举例
class A
{
protected:
int x;
}
int main( )
{
A a;
a.X=5; //错误
}

 

正确

class A {
protected:
int x;
}
class B: public A{
public:
void Function( );
};
void B:Function( )
{
x =5; //正确
}

错误

class A {
private:
int x;
}
class B: public A{
public:
void Function( );
};
void B:Function( )
{
x =5; //错误:不能访问基类中的私有成员
}

 

 

四 派生类的构造 函数和析构函数

 

  1 派生类的构造函数

  基类的构造函数不被继承,需要在派生类 中自行声明。

  声明构造函数时,只需要对本类中新增成 员进行初始化,对继承来的基类成员的初 始化由基类完成。

  

  也就是各自的构造函数各自干自己的活

 

  重要 构造函数的调用顺序

  

#include <iostream>
using namespace std;
class A
{
public:
A( ){cout<<"A Constructor"<<endl;}
};
class B:public A
{
public:
B( ){cout<<"B Constructor"<<endl;}
};
int main()
{ B b; reurn 0;
}

运行结果为 先A后B

 

定义派生类对象时,构造函数的调用顺序:

首先调用基类的构造函数;

再调用对象成员类的构造函数;

最后调用派生类的构造函数

 

注意别忘了对象成员

#include <iostream>
using namespace std;

class component
{
public:
    component() {cout << "construct component" << endl;};
    ~component() {cout<< "destruct component" << endl;};
};

class base
{
public:
    base() {cout << "construct base" << endl;};
    ~base() {cout << "destruct base" << endl;};
};

class derived
    :base
{
public:
    derived() {cout << "construct derived" << endl;};
    ~derived() {cout << "destruct derived" << endl;};
    component c;
};

int main()
{
    derived d;
    return 0;
}

运行结果为

construct base
construct component
construct derived
destruct derived
destruct component
destruct base

   2 派生类构造函数的构造规则

(1)在基类中定义了默认构造函数,且该默认构造函 数可以完成派生类对象中 基类成员的构造,则派生 类构造函数的定义可以省略对基类构造函数的显式 调用,而采用隐含调用。

  准确的说,默认构造函数就是在调用时不需要显示地传入实参的构造函数。

  可以不带形参,也可以有默认形参

  

 

  隐含调用就是看起来没有调用基类的构造函数。

#include <iostream>
using namespace std;
class Base
{ public:
Base() { a=0; }
Base( int i) { a=i; }
protected:
int a;
};
class Derived:public Base
{ public:
Derived() { b=0; }
Derived( int i) { b=i; }
void Print() { cout<<"a="<<a<<",b="<<b<<endl; }
private:
int b;
};
int main( )
{ Derived d1; Derived d2(12);
d1.Print(); d2.Print();
cin.ignore(); return 0;
}

00 和0 12

(2)当基类的构造函数使用一个或多个参数时,或者 基类的默认构造函数不能完成基类成员的构造时, 派生类必须定义构造函数,

  显式调用基类构造函数, 提供将参数传递给基类构造函数的途径。这时,派 生类构造函数的函数体可能为空,仅起到参数传递 作用。 这时需要用到“成员初始化列表”

#include <iostream>
using namespace std;
class A
{
public:
    A() { cout << "A Constructor1" << endl; }
    A(int i) { x1 = i; cout << "A Constructor2" << endl; }
    void dispa() { cout << "x1=" << x1 << endl; }
private:
    int x1;
};
class B :public A
{
public:
    B() { cout << "B Constructor1" << endl; }
    B(int i) :A(i + 10)//没加这个东西也不会报错
        //定义派生类构造函数时,传递参数到基类构造函数
    {
        x2 = i; cout << "B Constructor2" << endl;
    }
    void dispb()
    {
        dispa(); //调用基类成员函数
        cout << "x2=" << x2 << endl;
    }
private:
    int x2;
};
int main()
{
    B b(2);
    b.dispb();//12 2



    B b1;
    b1.dispb();//乱码
    return 0;
}

 

注意派生类中构造函数的写法。

 

//派生类构造函数显式调用基类构造函数
#include <iostream>
#include <cstring>
using namespace std;
//日期类
class date
{
private:
    int year, mon, day;
public:
    date(int y = 2011, int m = 10, int d = 1)
    {
        year = y; mon = m; day = d;
    }
    void Print()
    {
        cout << year << "-" << mon << "-" << day << endl;
    }
};
//学生基类
class Student
{
protected:
    int number; char name[20]; char sex;
public:
    Student()
    {
        number = 0;
        strcpy(name, "No name "); sex = 'M';
    }
    Student(int n, char *s, char x)
    {
        number = n;
        strcpy(name, s); sex = x;
    }
};
//大学生派生类
class UGStudent :public Student
{
public:
    UGStudent(int n, char *s, char x, int a, int y, int m, int d)
        :Student(n, s, x), birth(y, m, d)
    {
        age = a;
    }
    UGStudent()
    {
        age = 0;
    }
    void Print()
    {
        cout << "number:" << number << endl;
        cout << "name:" << name << endl;
        cout << "sex:" << sex << endl;
        cout << "age:" << age << endl;
        cout << "birthday:";
        birth.Print();
    }
private:
    int age;
    date birth;
};
int main()
{
    UGStudent st1;
    UGStudent st2(1001, "Zhang", 'F', 20, 1991, 6, 11);
    st1.Print();
    st2.Print();
    cin.ignore();
    return 0;
}
/*
number:0
name:No name
sex:M
age:0
birthday:2011-10-1
number:1001
name:Zhang
sex:F
age:20
birthday:1991-6-11
*/

 

注意大学生这个派生类中,有birth这个对象成员。

 

 

析构函数很简单。

 

五 多继承

 class 派生类名:继承方式1 基类名1,…,继承方式n 基类名n { 派生类新定义成员 };

 

多继承下构造函数调用顺序:

(1)先调用所有基类的构造函数,再调用派生类的构造函数。

(2)处于同一层次的各基类构造函数的调用顺序取决于定义 派生类时所指定的基类顺序,与派生类构造函数中所定义的成 员函数初始化列表顺序无关。

// 示例多继承方式下构造函数和析构函数的调用顺序。
#include <iostream>
using namespace std;
class A //定义基类A
{
public:
    A(int i) { a = i; cout << "A Constructor" << endl; }
    void disp() { cout << "a=" << a << endl; }
    ~A() { cout << "A Destructor" << endl; }
private:
    int a;
};
class B //定义基类B
{
public:
    B(int j) { b = j; cout << "B Constructor" << endl; }
    void disp() { cout << "b=" << b << endl; }
    ~B() { cout << "B Destructor" << endl; }
private:
    int b;
};
class C :public B, public A //定义A和B的派生类C。B在前,A在后
{
public:
    C(int k) :A(k + 2), B(k - 2) //包含基类成员初始化列表
    {
        c = k;
        cout << "C Constructor" << endl;
    }
    void disp()
    {
        //用类名加作用域运算符限定调用某个基类的同名成员
        A::disp();
        B::disp();
        cout << "c=" << c << endl;
    }
    ~C() { cout << "C Destructor" << endl; }
private:
    int c;
};
int main()
{
    C obj(10);
    //调用类C的成员函数disp
    obj.disp();
    cin.ignore();
    return 0;
}
/*
B Constructor
A Constructor
C Constructor
a=12
b=8
c=10

C Destructor
A Destructor
B Destructor

*/

 

多继承且有内嵌对象时的构造函数

1.调用基类构造函数,调用顺序按照它们 被继承时声明的顺序(从左向右)。

2.调用成员对象的构造函数,调用顺序按 照它们在类中声明的顺序。

3.派生类的构造函数体中的内容。

// 例 派生类构造函数举例(多继承,含有内嵌对象) 。
#include <iostream>
using namespace std;
class B1 //基类B1,构造函数有参数
{
public:
    B1(int i) { cout << "constructing B1 " << i << endl; }
};
class B2 //基类B2,构造函数有参数
{
public:
    B2(int j) { cout << "constructing B2 " << j << endl; }
};
class B3 //基类B3,构造函数无参数
{
public:
    B3(){ cout << "constructing B3 *" << endl; }
};
class C : public B2, public B1, public B3
{
public: //派生类的公有成员
//注意基类名的个数与顺序
//注意成员对象名的个数与顺序
    C(int a, int b, int c, int d) :
        B1(a), memberB2(d), memberB1(c), B2(b) { }
private: //派生类的私有对象成员
    B1 memberB1;
    B2 memberB2;
    B3 memberB3;
};
int main()
{
C obj(1,2,3,4);
cin.ignore();
return 0;
}
/*
constructing B2 2
constructing B1 1
constructing B3 *
constructing B1 3
constructing B2 4
constructing B3 *
*/

B1 memberB1;
B3 memberB3;
B2 memberB2;

最下面三个也是这个顺序了

 

 

再来一个顺序的例子

// 例题5.6,多继承范例
#include <iostream>
#include <cstring>
using namespace std;
//定义研究生基类
class GStudent
{
protected:
    int number; char name[20]; char sex;
public:
    GStudent(int n, char *s, char x)
    {
        number = n; strcpy(name, s); sex = x;
        cout << "Construct GStudent. " << endl;
    }
    ~GStudent()
    {
        cout << "Destruct GStudent. " << endl;
    }
};
//定义职员基类
class Emplyee
{
protected:
    char ename[20];
    char jobname[20];
public:
    Emplyee(char *sn, char *sj)
    {
        strcpy(ename, sn);
        strcpy(jobname, sj);
        cout << "Construct Employee. " << endl;
    }
    ~Emplyee()
    {
        cout << "Destruct Employee. " << endl;
    }
};
//定义在职研究生类,从两个基类派生
class GStudentHasJob :public GStudent, public Emplyee
{
public:
    GStudentHasJob(int n, char *s, char x, char *sj) :
        GStudent(n, s, x), Emplyee(s, sj)
    {
        cout << "Construct GStudentHasJob. " << endl;
    }
    ~GStudentHasJob()
    {
        cout << "Destruct GStudentHasJob. " << endl;
    }
    void Print()
    {
        cout << "number:" << number << endl;
        cout << "name:" << name << endl;
        cout << "sex:" << sex << endl;
        cout << "job:" << jobname << endl;
    }
};
int main()
{
    GStudentHasJob st(1001, "zhang", 'F', "teacher");
    st.Print();
    cin.ignore();
    return 0;
}
/*
Construct GStudent.
Construct Employee.
Construct GStudentHasJob.
number:1001
name:zhang
sex:F
job:teacher

Destruct GStudentHasJob.
Destruct Employee.
Destruct GStudent.
*/

 

 

六 基类成员访问 和赋值兼容性

 1 基类成员名的限定访问 和同名覆盖

  在多继承中出现?

  

  当派生类的多个基类存在同名成员时: 则派生类对些同名成员的访问就可能存在冲 突

  解决办法:显式调用要访问的成员,成员名 限定访问法 基类名::成员名

 2 同名覆盖原则

  当派生类与基类中有相同成员时: 若未强行指名,则通过派生类对象使用的 是派生类中的同名成员。

  如要通过派生类对象访问基类中被覆盖的 同名成员,应使用基类名限定//同上

  

// 多继承同名覆盖举例
#include <iostream>
using namespace std;
class B1 //声明基类B1
{
public: //外部接口
    int nV;
    void fun(){ cout << "Member of B1" << endl; }
};
class B2 //声明基类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; }
        //同名函数成员
};
int main()
{ D1 d1;
d1.nV = 1; //对象名.成员名标识, 访问D1类成员
d1.fun();
d1.B1::nV = 2; //作用域分辨符标识, 访问基类B1成员
d1.B1::fun();
d1.B2::nV = 3; //作用域分辨符标识, 访问基类B2成员
d1.B2::fun();
return 0;
}
/*
Member of D1
Member of B1
Member of B2
*/

  3 二义性问题

  同名覆盖,无声明就二义性  

 

在多继承时,基类与派生类之间,或基类之间出现同 名成员时,将出现访问时的二义性(不确定性)—— 采用虚函数或支配(同名覆盖)原则来解决。

当派生类从多个基类派生,而这些基类又从同一个基 类派生,则在访问此共同基类中的成员时,将产生二 义性——采用虚基类来解决。

// 二义性问题举例
class A
{
public:
void f( );
};
class B
{
public:
void f( );
void g( );
};
class C: public A, piblic B
{ public:
void g( );
void h( );
};
如果声明:C c1;
则 c1.f( ); 具有二义性
而 c1.g( ); 无二义性(同名覆
盖)

  解决方法一:用类名来限定 c1.A::f( ) 或 c1.B::f( )

  解决方法二:同名覆盖 在类C 中声明一个同名成员函数f( ),f( ) 再根据需要调用 A::f( ) 或 B::f( )

 

 

再来举一个例子

 

这个例子还是比较特别的

class B
{ public:
int b;
}
class B1 : public B
{
private:
int b1;
}
class B2 : public B
{
private:
int b2;
};
class C : public B1,public B2
{
public:
int f( );
private:
int d;
}

下面的访问是二义性的: C c;

           c.b c.B::b

下面是正确的: c.B1::b

        c.B2::b

  4 赋值兼容规则

派生类对象可以看作是基类对象:派生类包含了基类的 全部成员;

基类对象不能被看作是派生类对象:派生类新增了成员

基类对象可以赋值给基类对象,也可以把派生类对象 赋值给基类对象

基类指针可以指向基类对象,也可以指向派生类对象

基类引用可以指向基类对象,也可以指向派生类对象

 

 

一个公有派生类的对象在使用上可以被当作基类的 对象,反之则禁止。

具体表现在:

派生类的对象可以被赋值给基类对象。

派生类的对象可以初始化基类的引用。

指向基类的指针也可以指向派生类。

疑问:为什么是公有派生类? 因为公有派生类和基类之间可以使用相同的函数,而 没有必要为每一个类设计单独的模块

 

#include <iostream>
using namespace std;
class B0 //基类B0声明
{
public:
    void display() { cout << "B0::display( )" << endl; }
    //公有成员函数
};
class B1 : public B0
{
public:
    void display() { cout << "B1::display( )" << endl; }
};
class D1 : public B1
{
public:
    void display() { cout << "D1::display( )" << endl; }
};
void fun(B0 *ptr)
{
    ptr->display();
} //"对象指针->成员名"
int main() //主函数
{

B0 b0; //声明B0类对象
B1 b1; //声明B1类对象
D1 d1; //声明D1类对象
B0 *p; //声明B0类指针
p = &b0; //B0类指针指向B0类对象
fun(p);
p = &b1; //B0类指针指向B1类对象
p->display();
cout << 1 << endl;
fun(p);
p = &d1; //B0类指针指向D1类对象
p->display();
cout << 1 << endl;
fun(p);
return 0;


}
/*

B0::display( )
B0::display( )
1
B0::display( )
B0::display( )
1
B0::display( )

*/

同样的例子

// 例题5.10,赋值兼容实例
#include <iostream>
using namespace std;
class Base
{
protected:
    int member;
public:
    Base()
    {
        member = 0;
    }
    void Show()
    {
        cout << "Base::Show():" << member << endl;
    }
};
class Derived1 :public Base
{
public:
    Derived1(int a)
    {
        member = a;
    }
    void Show()
    {
        cout << "Derived1::Show():" << member << endl;
    }
};
class Derived2 :public Derived1
{
public:
    Derived2(int a) :Derived1(a)
    { }
    void Show()
    {
        cout << "Derived2::Show():" << member << endl;
    }
};
void Test(Base *pb)
{
    pb->Show();
}
void Test(Base &br)
{
    br.Show();
}

int main()
{
    Base b0;
    Derived1 d1(5);
    Derived2 d2(10);
    Base *pb0;
    pb0 = &b0;
    Test(pb0);
    b0 = d1;
    Test(pb0);
    pb0 = &d1;
    Test(pb0);
    Test(d2);
    cin.ignore(); return 0;
}
/*
Base::Show():0
Base::Show():5
Base::Show():5
Base::Show():10

*/

 

七 虚基类

一张图说明多继承的冗余

 

  虚基类的定义

  class 派生类名:virtual 继承方式 共同基类名

  在派生类的对象中,这些同名成员在内存中 同时拥有多个副本,如果将直接基类的共同 基类设置为虚基类,

那么从不同的路径继承 过来的该类成员(包括数据成员和函数成员) 在内存中只拥有一个副本(数据成员只

有一 个副本,函数成员只有一个映射),从而解 决了同名成员的唯一标识问题

 

  其实就是上面的6-3.二义性问题的最后一个例子

 

引入虚基类之后就这样了:

代码示例

// 示例虚基类。
#include <iostream>
using namespace std;
class A
{
public:
    A() { a = 10; }
protected:
    int a;
};
class A1 :virtual public A
    //定义虚基类
{
public:
    A1() { cout << a << endl; }
};
class A2 :virtual public A
    //定义虚基类
{
public:
    A2() { cout << a << endl; }
};
class B :A1, A2 //私有继承
{
public:
    B() { cout << a << endl; }
};
int main()
{
    B obj;
    return 0;
}
/*
10
10
10*/

 

  虚基类的初始化

  构造函数的调用顺序

  先调用虚基类的构造函数,再调用非虚基类的 构造函数。

  若同一层次中包含多个虚基类,其调用顺序按定 义时顺序。

  若虚基类由非虚基类派生而来,则仍按先调用基 类构造函数,再调用派生类构造函数的顺序

代码示例

// 示例引入虚基类后构造函数的调用顺序。
#include <iostream>
using namespace std;
class Base1
{
public:
    Base1() { cout << "class Base1" << endl; }
};
class Base2
{
public:
    Base2() { cout << "class Base2" << endl; }
};
class Level1 :public Base2, virtual public Base1 //定义虚基类
{
public:
    Level1() { cout << "class Level1" << endl; }
};
class Level2 :public Base2, virtual public Base1
{
public:
    Level2() { cout << "class Level2" << endl; }
};
class TopLevel :public Level1, virtual public Level2
{
public:
    TopLevel() { cout << "class TopLevel" << endl; }
};
int main()
{
    TopLevel obj;
    return 0;
}
/*
class Base1
class Base2
class Level2
class Base2
class Level1
class TopLevel
*/

 

 

这里就是先调用Toplevel的构造函数,先虚基类level2,再level1

 

level2中先虚基类base1,输出class base1,再base2构造函数,输出class base2

然后level2的本身的calss level2

 

level1中(先虚基类base1,输出class base1)  错错错,没了,虚基类只有一次,再base2,输出class base2

然后level1的本身的class level1

最后本身的构造函数

 

 

换个角度讲,虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身。

 

如果加上

Level1 a;
Level2 b;

结果会是

class Base1
class Base2
class Level1
class Base1
class Base2
class Level2

所以说,虚基类就是保证在多继承的类似的菱形路线上,后代的后代的身体(类的内存结构上),一个虚基类只会最多生成一次

emmm,感觉就是这样的,

 

 

 

注意:定义虚基类的构造函数注意:

(1)一般情况下虚基类只允许定义不带参数的或带 缺省参数的构造函数

(2)如果多继承不牵扯到对同一基类的派生,就没 必要定义虚基类。

 

虚基类的初始化与一般多继承的初始化规则如下:

1.所有从虚基类直接或间接派生的类,都必须在该类 构造函数的成员初始化列表中列出对虚基类构造函数 的调用。

   但是只有实际构造对象的类的构造函数才会 引发对虚基类构造函数的调用,而其他基类在成员初 始化列表中对

   虚基类构造函数的调用都会被忽略,这 样将确保派生类对象中虚基类成员只被初始化一次。

2.如果某类构造函数的成员初始化列表中同时列出对 虚基类构造函数和非虚基类构造函数的调用,则会优 先执行

   虚基类构造函数。

 

// 示例虚基类的应用。
#include <iostream>
#include <string.h>
using namespace std;
class Data_rec
    //定义基类Data_rec
{
public:
    Data_rec()
    {
        name = NULL;
    }
    ~Data_rec()
    {
        delete[] name;
    }
    void insert_name(char* pname)
    {
        delete[] name;
        name = new char[strlen(pname) + 1];
        strcpy(name, pname);
    }
    void print()
    {
        cout << "Name:" << name << endl;
    }
private:
    char* name;
};
class Student :virtual public Data_rec //定义虚基类
{
public:
    Student() :Data_rec() { id = NULL; }
    ~Student() { delete[] id; }
    void insert_id(char* pid)
    {
        delete[] id;
        id = new char[strlen(pid) + 1];
        strcpy(id, pid);
    }
    void print()
    {
        Data_rec::print(); //访问基类的成员函数
        cout << "Id:" << id << endl;
    }
private:
    char* id;
};
class Teacher :virtual public Data_rec //定义虚基类
{
public:
    Teacher() :Data_rec() { sal = 0; }
    void insert_sal(float psal) { sal = psal; }
    void print()
    {
        Data_rec::print(); //访问基类的成员函数
        cout << "Sal:" << sal << endl;
    }
private:
    float sal;
};
class Postgrad :public Student //定义派生类Postgrad
{
public:
    Postgrad() :Student() { dn = NULL; }
    void insert_dn(char* p)
    {
        delete[] dn;
        dn = new char[strlen(p) + 1];
        strcpy(dn, p);
    }
    void print()
    {
        Student::print(); //访问基类的成员函数
        cout << "Dept Name:" << dn << endl;
    }
private:
    char* dn;
};
class Tpost :public Teacher, public Postgrad //定义多继承派生类Tpost
{
public:
    Tpost() :Teacher(), Postgrad() {}
    void print()
    {
        Teacher::print();
        Postgrad::print();
    }
};
int main()
{
    Teacher tobj;
    Tpost tpobj;
    tobj.insert_name("Li Min");
    tobj.insert_sal(2000);
    tpobj.insert_name("Zhang Hua");
    tpobj.insert_sal(1500);
    tpobj.insert_id("03410101");
    tpobj.insert_dn("Computer");
    tobj.print();
    tpobj.print();
    cin.ignore();
    return 0;
}
/*
Name:Li Min
Sal:2000
Name:Zhang Hua
Sal:1500
Name:Zhang Hua
Id:03410101
Dept Name:Computer
*/

 这个例子,,没看

 

 

虚基类的一个重要例子还没有理解

 

#include <iostream>
using namespace std;

class transportation
{
public:
    transportation(int i)
    {
        cout<<"transportation "<<i<<" born"<<endl;
    }
};
class car:virtual public transportation
{
public:
    car(int i):transportation(i)
    {
        cout<<"car "<<i<<" born"<<endl;
    }
};
class plane:virtual public transportation
{
public:
    plane(int i):transportation(i)
    {
        cout<<"plane "<<i<<" born"<<endl;
    }
};
class carplane:public car,public plane
{
public:
    carplane(int i ):car(i+2),plane(i+3)
    {
        cout<<"carplane "<<i<<" born"<<endl;
    }
};
int main()
{
    carplane t(0);
    return 0;
}
/*程序输出
transportation 1 born
car 2 born
plane 3 born
carplane 0 born
*/
/*如果没有使用虚继承的方式,则程序输入为:
transportation 2 born
car 2 born
transportation 3 born
plane 3 born
carplane 0 born
*/

 

 感觉只是我单纯地忘记了派生类构造函数执行之前,必定先执行基类构造函数。

 

posted @ 2019-11-21 17:42  TheDa  阅读(1079)  评论(0编辑  收藏  举报