一些类的基础可以查看
C++核心编程

类做类成员的一些练习

  1. #include <iostream>
  2. using namespace std;
  3. class X
  4. {
  5. private:
  6. int x, y;
  7. public:
  8. X(int i,int j)
  9. {
  10. x = i;
  11. y = j;
  12. }
  13. void Print()
  14. {
  15. cout << x << " " << y << endl;
  16. }
  17. };
  18. class Y
  19. {
  20. X a;
  21. public:
  22. Y(int i,int j):a(i,j){}
  23. void Print()
  24. {
  25. a.Print();
  26. }
  27. };
  28. int main()
  29. {
  30. Y y(1, 2);
  31. y.Print();
  32. return 0;
  33. }
  1. #include <iostream>
  2. using namespace std;
  3. class A
  4. {
  5. int a;
  6. public:
  7. A()
  8. {
  9. a = 0;
  10. cout << "A default constructor called!" << a << endl;
  11. }
  12. A(int i)
  13. {
  14. a = i;
  15. cout << "A constructor called!" << a << endl;
  16. }
  17. ~A()
  18. {
  19. cout << "A destructor called!" << a << endl;
  20. }
  21. int Returna()
  22. {
  23. return a;
  24. }
  25. };
  26. class B
  27. {
  28. A a1, a2;
  29. int b;
  30. int& b1;
  31. const int b2;
  32. public:
  33. B():b1(b),b2(0)
  34. {
  35. b = 0;
  36. cout << "B default constructor called!" << b << endl;
  37. }
  38. B(int i,int j,int k,int l):a2(j),a1(i),b2(k),b1(b) //构造函数初始化列表中书写的顺序与子对象构造的顺序没有关系,与它们定义的先后顺序有关
  39. {
  40. b = l;
  41. cout << "B constructor called!" << b << endl;
  42. }
  43. ~B()
  44. {
  45. cout << "B destructor called!" << b << endl;
  46. }
  47. void Print()
  48. {
  49. cout << a1.Returna() << " " << a2.Returna() << endl;
  50. cout << b << " " << b1 << " " << b2 << endl;
  51. }
  52. };
  53. int main()
  54. {
  55. B X(1, 2, 3, 4);
  56. // B X;
  57. X.Print();
  58. return 0;
  59. }

局部类

定义在一个函数内的类叫做局部类,只能在定义它的函数体内使用,超出函数体不可见

  1. 在局部类中所有的成员函数都必须定义在类里
  2. 局部类中不能说明静态成员 ```cpp

    include

    using namespace std;

void func() { int a(8); class A { int a; public: A(int i) { a = i; } void Print() { cout << “a=” << a << endl; } }; A m(5); m.Print(); cout << a << endl; }

int main() { int a(3); func(); cout << a << endl; return 0; }

  1. 练习:<br />建立一个对象数组,内放5个学生数据(学号是字符串类型,成绩是整型)<br />设立Max函数,用指向对象的指针作为函数的参数,在Max中找出5个学生中成绩最高的,并输出其学号和成立<br />输入:<br />输入5个学生的数据<br />输出:<br />输出5个学生中成绩最高的学号和成绩<br />样例输入:<br />01 89<br />02 78<br />03 56<br />04 92<br />05 76<br />样例输出:<br />04 92
  2. ```cpp
  3. #include <iostream>
  4. #pragma warning(disable:4996)
  5. using namespace std;
  6. class student
  7. {
  8. public:
  9. char num[20];
  10. int score;
  11. };
  12. student a[5];
  13. void Max(student * &p)
  14. {
  15. int max = a[0].score;
  16. for(int i=1;i<5;i++)
  17. {
  18. if(a[i].score>max)
  19. {
  20. max = a[i].score;
  21. p = &a[i];
  22. }
  23. }
  24. }
  25. int main()
  26. {
  27. int i;
  28. for (i = 0; i < 5; i++)
  29. cin >> a[i].num >> a[i].score;
  30. student* p = &a[0];
  31. Max(p);
  32. cout << p->num << " "<<p->score << endl;
  33. return 0;
  34. }

堆对象

定义

堆对象是一种在运行期间根据需要创建的动态对象,通常放在内存的堆区,这个区域的变量可以根据需要随时被删除,创建和删除堆对象使用运算符new和delete
new和delete是c++的语法

使用运算符new创建变量的格式

语法:new 类型(初始值表)
其中,初始值表可以省略该运算符的表达式是一个地址值,通常将它赋给一个同类型的指针
例:int *p=new int(8)
在堆上面分配了一个可存放int类型数据的大小的空间,同时初始化为8

释放变量的内存:`delete 指针名`<br />    例如:`delete p;`

用new运算符创建数组的格式

语法:new 类型[大小]
其中,大小表示数组的元素的个数,该表达式返回一个地址值,通常赋给一个同类型的指针,该指针指向数组的第一个元素
例:

int *pa;
pa=new int[5];
pa[0]=1;
pa[1]=2;
pa[2]=3;
pa[3]=4;
pa[4]=5;

释放数组内存:delete [] 指针名
例:delete[] pa
示例:

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;

int main()
{
    int* p, * pa;
    p = new int(10);
    pa = new int[10];
    if(!pa)
    {
        cout << "error!" << endl;
        exit(1);
    }
    for (int i = 0; i < 10; i++)
        pa[i] = i + 10;
    for (int i = 0; i < 10; i++)
        cout << pa[i] + *pa << " ";
    cout << endl;
    delete p;
    delete[] pa;
    return 0;
}

用new运算符创建堆对象和delete堆对象的格式

创建堆对象
new 类名(初始值)
创建堆对象时可同时调用相应的构造函数对对象进行初始化后返回一个指向该对象的指针
例:

class A
{
public :
    A(int i,int j){...}
        ...
};

A *pa=new A(3,4);//new后面接的是类的构造函数
pa是一个指向该类A对象的指针

释放该对象:
    delete pa

练习

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;
class A
{
    int a1, a2;
    public:
    A(int i,int j)
    {
        a1 = i;
        a2 = j;
        cout << "constructor called!" << endl;
    }
    ~A()
    {
        cout << "destructor called!" << endl;
    }
    void Print()
    {
        cout << a1 << " " << a2 << endl;
    }
};

int main()
{
    A* pa1, * pa2;
    pa1 = new A(1, 2);
    pa2 = new A(3, 4);
    pa1->Print();
    pa2->Print();
    delete pa1;
    delete pa2;
    return 0;
}

创建堆对象数组

#include <iostream>
using namespace std;
class B
{
    char name[80];
    double b;
public:
    B()
    {
        cout << "destructor called!" << endl;
    }
    B(const char* s, double n)
    {
        strcpy(name, s);
        b = n;
        cout << "constructor called!" << endl;
    }
    ~B()
    {
        cout << "destructor caleld!" << endl;
    }
    void Getb(char *s,double &n)
    {
        strcpy(s, name);
        n = b;
    }
};
int main()
{
    B* p;
    double n;
    char s[80];
    p = new B[5];
    p[0] = B("wang", 5.6);
    p[1] = B("zhang", 1.2);
    p[2] = B("li", 8.2);
    *(p + 3) = B("ma", 1.2);
    *(p + 4) = B("niu", 7.8);
    for(int i=0;i<5;i++)
    {
        p[i].Getb(s, n);
        cout << s << " " << n << endl;
    }
    delete[] p;
    return 0;
}

练习
任意输入一个字符串,串中连续出现的相同元素构造的子序列称为平台,试建立一个类Str,求出串中最长平台的长度,并能输出最长平台的子序列。
例如:字符串为”jkkkaabbbbbaaa”,最长平台长度为5,子序列为”bbbbb”.具体要求如下:
(1).私有数据成员:
char str:指向动态申请的字符空间
char
s:存放最长平台子字符串
int maxlen:str所指向的字符串中最长平台的长度
(2).公有成员函数:
Str(char *p):构造函数,动态申请成员str指向的内存空间,用p指向的字符串初始化str指向的字符串,置maxlen初始值为0,p缺省为空指针。
~Str():释放str所指向的动态内存空间
void Process():求str所指向的字符串最长平台子串及最长平台的长度
void Show():输出字符串、最长平台子串及最长平台的长度
(3).在主函数中完成对该类的测试。
输入一个字符串到字符数组text中,定义一个Str类的对象s,用text初始化对象s,调用成员函数求str所指向字符串中最长平台的长度,输出字符串及最长平台的长度。

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;

class Str
{
    char* str, * s; //str指向动态申请的字符空间 ,s存放最长平台子字符串,
    int maxlen;//maxlen存放str所指向的字符串中最长平台的长度
public:
    Str(char *p)   //动态申请成员str指向的内存空间,用p指向的字符串初始化str指向的字符串,置maxlen初始值为0,p缺省为空指针。
    {
        str = new char[strlen(p) + 1];
        s = new char[strlen(p) + 1];
        strcpy(str, p);
        maxlen = 0;
    }
    ~Str() //释放str所指向的动态内存空间
    {
        delete[] str;//                                    num          5
        delete[] s;  //                                                 iiiii
    }//                                                    temp:        bbbbb
     //                                                                 jjjjjjjjjjj
    void Process() //求str所指向的字符串最长平台子串及最长平台的长度    kkkaabbbbbaaa
    {
        int j = 0,num,i;
        char temp[100] = { '\0' };
        while(str[j])
        {
            num = 1;
            i = 0;
            while(str[j]==str[j+1])
            {
                num++;
                temp[i++] = str[j++];
            }
            temp[i] = str[j++];
            if(num>maxlen)
            {
                maxlen = num;
                strcpy(s, temp);
            }
        }
    }
    void Show()  //:输出字符串、最长平台子串及最长平台的长度
    {
        cout << str << endl;
        cout << s << endl;
        cout << maxlen << endl;
    }
};
int main()
{
    char text[100];
    cin >> text;
    Str s(text);  //char *p=text;
    s.Process();
    s.Show();
    return 0;
}

深拷贝与浅拷贝

#include <iostream>
using namespace std;

class A
{
    char *name;
    int age;
public:
    A(char* s, int a)
    {
        name = new char[20];
        strcpy(name, s);
        age = a;
    }
    void Set(char *s,int a)
    {
        strcpy(name, s);
        age = a;
    }
    void Print()
    {
        cout << name << " " << age << endl;
    }
    A(A &a)    //深拷贝 :在拷贝的时候给数据成员中的指针变量所指向的对象单独开辟了一段内存
    {
        name = new char[20];
        strcpy(name, a.name);
        age = a.age;
    }
};
int main()
{
    char name[20] = "Black";
    A a(name, 18);
    //a.Print();
    A b = a;   //浅拷贝,只是单纯的拷贝了数据成员中指针变量的值
//    b.Print();
    char name2[20] = "Awayken";
    a.Set(name2, 20);
    a.Print();    //Awayken 20             Awayken 18         Awayken 20
    b.Print();    //Awayken 18             Awayken 18         Black 18
    return 0;
}

继承

1.基类和派生类
继承的实质就是创建新的类
假定有一个类,它有一定的属性和行为,称它为基类
在该类的基础上定义一个新类,而新类首先具有基类中已有的属性和行为,称它继承了基类
,另外新类还有它自己的某些属性和行为,则新类叫派生类,派生类与基类之间的关系称为继承关系.
简单的说,新类继承了基类,反过来说,基类派生类新类,通俗的讲,基类为父类,新类即派生类为子类.

2.继承的好处
继承的好处是在已有类的基础上快速的创建一个新类,提高了代码的复用性

3.继承分为单一继承和多重继承
1).单一继承
派生类只有一个基类的基础
2).多重继承
派生类有2个或2个以上的基类

4.单一继承
1).单一继承的定义格式

class 派生类名:继承方式 基类名
{
    派生类中新成员的定义
};

2).继承方式有3中
public:公有继承
1.父类的private成员在子类中不可访问,
2.父类的public和protected成员在子类中仍然是原有的属性

private:私有继承
1.父类的private成员在子类中不可访问,
2.父类的public和protected成员在子类中都为private属性

protected:保护继承<br />    1.父类的private成员在子类中不可访问,<br />    2.父类的public和protected成员在子类中都为protected属性

注意:<br />    派生类的成员函数不可访问直接基类和间接基类的私有成员,
#include <iostream>
using namespace std;

class Base
{
private:
    int b1;//私有成员,子类也不可以访问
protected:
    int b2;//保护成员,子类可以访问,类外不可以访问
public:
    int b3;
};

class D1 :private Base
{
private:
    int d1;
protected:
    int d2;
public:
    int d3;
    void Set()
    {
    //    b1 = 1;//b1不可以访问,因为是base的私有成员
        b2 = 2;
        b3 = 3;
        d1 = 4;
        d2 = 5;
        d3 = 6;
    }
};

class D2:public D1
{
public:
    void Set()
    {
        b3 = 10;
    }
};

int main()
{
    D1 d;
    d.b3 = 10;

    return 0;
}

派生类对父类产生的影响

1.添加新成员(包括数据成员和成员函数)
2.重新定义了已有的成员函数
3.改变现有成员的属性

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;

class Base
{
private:
    int b1;
protected:
    int b3;
public:
    int d2;
};

class D1 :public Base
{
private:
    int d1;
protected:
    int d3;
public:
    int d2;

    void Set()
    {
        //    b1 = 1;   父类的私有成员在子类中不可访问
        Base::d2 = 2;  //当基类与子类有同名数据成员,在子类里默认操作的是子类的数据,如要调用基类中的,需要专门指出
        b3 = 3;
        d1 = 4;
        d2 = 5;
        d3 = 6;
    }
    void Print()
    {
        cout << " Base::d2=" << Base::d2
            << "b3=" << b3 
            << " d1=" << d1 
            << " d2=" << d2 
            << " d3=" << d3 << endl;
    }
};

class D2 :public D1
{
public:
    int d2;
    void Set()//基类和子类可以具体同名的成员函数,子类对象默认调用的是子类的成员函数
    {
        //    b1 = 10;
        Base::d2 = 10;//用作用域指明用的是Base中的d2变量
        b3 = 30;
        //    d1 = 40;
        D1::d2 = 50;//用作用域指明用的是D1中的d2变量否则用的是D2自己的
        d3 = 60;
        d2 = 70;
    }
    void Print()
    {
        cout << " Base::d2=" << Base::d2 
            << " b3=" << b3 
            << " D1::d2=" << D1::d2
            << " d3=" << d3 
            << " D2::d2=" << d2 << endl;
    }
};

int main()
{
    D1 d1;
    d1.Set();//调用的是D1的Set()成员函数
    d1.Print();//调用的是D1的Print()成员函数
    D2 d2;
    d2.Set();//调用的是D2的Set()成员函数
    d2.Print();//调用的是D2的Print()成员函数
    d2.D1::Print();//调用的是D1的Print()成员函数,这里输出的d1会是乱码是因为d2没有为d1设值

    return 0;
}

image.png

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;
class A
{
protected:
    int a;
    void fa(int i)
    {
        a = i;
    }
    void ga()
    {
        cout << "A" << endl;
    }
};

class B :protected A   //在不写继承方式的时候,class默认是私有继承,struct默认公有继承
{
public:
    void hb()
    {
        cout << "B" << endl;
    }
    using A::fa;  //说明成员函数fa是公有的
    using A::a;//这个方法可以把父类保护的成员变量和函数变为共有,如果是私有的,这种方法也无法调用
};

int main()
{
    B b;
    b.fa(5);
    cout << b.a << endl;
    b.hb();

    return 0;
}

//注意:如果基类的成员是私有的,则在子类中不能改变它的属性

单一继承派生类的构造函数和析构函数

#include <iostream>
using namespace std;
class A    // a
{
private:
    int a;
public:
    A()
    {
        a = 0;
        cout << "default constructor called! a=" << a << endl;
    }
    A(int i)
    {
        a = i;
        cout << "consrtuctor called a=" << a << endl;
    }
};

class B :public A  // a
{                  // b
private:
    int b;
public:
    B()
    {
        b = 1;
        cout << "default constructor called b=" << b << endl;
    }
    B(int i):A(i)
    {
        b = i;
        cout << "constructor called b=" << b << endl;
    }
};

int main()
{
//    B b1;
    B b2(3);

    return 0;
}
  1. 用子类创建子类对象的时候会调用子类的构造函数,在执行子类构造函数之前会调用并执行基类的构造函数完成子类对象中基类成员的数据构造,如果在子类的构造函数中没有调用基类的构造函数则会去自动调用基类的默认构造函数
  2. 如果想通过子类的构造函数调用基类的带参数的构造函数实现子类对象中基类数据成员的构造,可以在子类带参数的构造函数的初始化列表中写上基类的构造函数

例如:B(int i) : A(i){}

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;
class A    // a
{
private:
    int a;
public:
    A()
    {
        a = 0;
        cout << "default constructor called! a=" << a << endl;
    }
    A(int i)
    {
        a = i;
        cout << "consrtuctor called a=" << a << endl;
    }
    ~A()
    {
        cout << "A destructor called" << endl;
    }
    void Print()
    {
        cout << a << " ";
    }
    int Geta()
    {
        return a;
    }
};
class B :public A  // a
{                  // b
private:
    int b;
    A aa;
public:
    B()
    {
        b = 1;
        cout << "default constructor called b=" << b << endl;
    }
    B(int i,int j,int k):aa(j),A(i),b(k)
    {
        cout << "constructor called b=" << b << endl;
    }
    ~B()
    {
        cout << "B destructor called" << endl;
    }
    void Print()
    {
        A::Print(); //基类里的a
        cout << "b=" << b << "  b.a=" << aa.Geta() << endl;
    }
};

int main()
{
    B bb[2];
    bb[0] = B(1, 2, 3);
    bb[1] = B(4, 5, 6);
    for (int i = 0; i < 2; i++)
        bb[i].Print();
    return 0;
}

派生类构造函数调用顺序

  1. 基类构造函数,构造子类中属于基类的部分
  2. 调用基类的构造函数,构造子类中的子对象
  3. 成员函数初始化列表中其它初始化项
  4. 派生类的构造函数的函数体

    练习

    把描述直接坐标系上的一个点的类作为基类,派生出描述一条直接的类和描述三角形的类。定义成员函数求出两点之间的距离和三角形的面积。
    提示:
    先定义描述点的类Point;
    类Point类派生类Line为直线类,一直线有两个端点,所以它在点类的基础上新增一组点的坐标(x2,y2);
    三角形类T在直线的基础上再新增一组点的坐标(x3,y3),求出三角形的面积。具体要求如下:
    (1).定义点类Point
    int x1,y1 ; //保护的数据成员
    公有构造函数Point(int a,int b); //初始化x1,y1
    (2).定义直线类Line
    int x2,y2; //保护的数据成员
    公有构造函数Line(int a,int b,int c,int d); //初始化x2,y2以及x1,y1
    (3).定义三角形类Triangle
    int x3,y3; //私有数据成员
    double area; //私有数据成员(面积)
    公有构造函数Triangle(int a,int b,int c,int d,int e,int f)://初始化x3,y3,x1,y1,x2,y2
    void f(); //求三角形面积的功能函数,先求出三条边x,y,z,然后用海伦公式求解面积:
    s=(x+y+z)/2
    area=sqrt(s(s-x)(s-y)*(s-z));
    void Print(); //输出三个点的坐标和面积
    (4).在主函数中对该类进行测试。
    定义一个Triangle类的对象tri,以1和1,4和1,4和5作为点的坐标,完成测试。程序运行输出:
    (1,1) (4,1) (4,5)
    area=6 ```cpp

    include

    pragma warning(disable:4996)

    using namespace std;

class Point { protected: int x1, y1; public: Point(int a = 0, int b = 0) { x1 = a; y1 = b; } }; class Line :public Point { protected: int x2, y2; public: Line(int a, int b, int c, int d) :Point(a, b) { x2 = c; y2 = d; } float dis() { float t = (x2 - x1) (x2 - x1) + (y2 - y1) (y2 - y1); return sqrt(t); } }; class Triangle :public Line { int x3, y3; double area; public: Triangle(int a, int b, int c, int d, int e, int f) :Line(a, b, c, d) { x3 = e; y3 = f; } void Area() { Line ln1(x3, y3, x2, y2), ln2(x3, y3, x1, y1); float x = ln1.dis(), y = ln2.dis(), z = dis(); float s = (x + y + z) / 2.0; area = sqrt(s (s - x) (s - y) * (s - z)); } void Print() { cout << “(“ << x1 << “,” << y1 << “)” << endl; cout << “(“ << x2 << “,” << y2 << “)” << endl; cout << “(“ << x3 << “,” << y3 << “)” << endl; cout << “area=” << area << endl; } }; int main() { Triangle tr(1, 1, 4, 1, 4, 5); tr.Area(); tr.Print(); return 0; }

<a name="okUJk"></a>
## 子类型和赋值兼容规则
同一个类的对象间可以相互赋值,不同类的对象只有在某种条件下可以赋值<br />赋值兼容规则指出在公有继承下,派生类的对象可以作为基类的对象来使用,<br />1.派生类的对象可以赋值给基类的对象
```cpp
D类公有继承于B类
D d;
B b;
b=d;

2.派生类的对象可以用来初始化基类对象的引用

D d;
B &rb=d;

3.派生类对象的地址可用来给指向基类的对象的指针赋值

D d;
B *pb=&d;
#include <iostream>
using namespace std;
class M
{
    int m;
public:
    M()
    {
        m = 0;
    }
    M(int i)
    {
        m = i;
    }
    void Print()
    {
        cout << m << endl;
    }
    int Getm()
    {
        return m;
    }
};
class N :public M
{
    int n;
public:
    N()
    {
        n = 0;
    }
    N(int i, int j) :M(i), n(j)
    {}
    void Print()
    {
        M::Print();
        cout << n << endl;
    }
};
void fun(M& p)
{
    cout << p.Getm() << endl;
    p.Print();
}
int main()
{
    M m(1), q;
    N n(2, 3);
    n.Print();//2 3
    M* pm = new M(4);
    N* pn = new N(5, 6);
    pm = pn;
    pm->Print();    //  5
    cout << "111111" << endl;
    fun(*pn);//5    // 5
    cout << "22222" << endl;
    N n1(7, 8);
    M& rm = n1;
    n1.Print();//7 8
    cout << "33333" << endl;
    rm.Print();    //  7
    return 0;
}

多重继承:

1).定义
多重继承与单一继承的区别进在于基类的数目,一个基类的继承是单一继承,多个基类的继承是多重继承.
多重继承中由于基类数目的增加,对于构造函数的调用带来相应复杂性.
格式如下:

class 派生类名:继承方式1 基类名1,继承方式2 基类名2,....
{
    派生类的类体
};

例:D类是派生类,它有3个基类B1,B2,B3都是公有继承,定义的格式如下:

class B1
{...};
class B2
{...};
class B3
{...};
class D:public B1,public B2,public B3
{...}

派生类D中包含了基类B1,B2,B3的所有的成员,还包含自身定义的成员

2).多重继承派生类的构造函数
多重继承派生类的构造函数与单一继承的构造函数的区别:
主要是多重继承构造函数成员初始化列表中应包含所有基类的构造函数.格式如下:

派生类构造函数(参数表):基类名1(参数表1),基类名2(参数表2)...
{
    派生类构造函数体
}

多重继承派生类的构造函数执行顺序与单一继承派生类的构造函数执行顺序相似
先执行所有基类的构造函数,在多个基类的构造函数中按派生类被定义时的基类的顺序依次执行
与成员初始化表中给定的基类顺序无关,再执行子对象类的构造函数,租后执行该派生类构造函数的函数体

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;
class B1
{
private:
    int b1;
public:
    B1(int i)
    {
        b1 = i;
        cout << "construcor B1.b1=" << b1 << endl;
    }
    void Print()
    {
        cout << b1 << endl;
    }
    ~B1()
    {
        cout << "destructor B1.b1=" << b1 << endl;
    }
};
class B2
{
private:
    int b2;
public:
    B2(int i)
    {
        b2 = i;
        cout << "construcor B2.b2=" << b2 << endl;
    }
    void Print()
    {
        cout << b2 << endl;
    }
    ~B2()
    {
        cout << "destructor B2.b2=" << b2 << endl;
    }
};
class B3
{
private:
    int b3;
public:
    B3(int i)
    {
        b3 = i;
        cout << "construcor B3.b3=" << b3 << endl;
    }
    void Print()
    {
        cout << b3 << endl;
    }
    ~B3()
    {
        cout << "destructor B3.b3=" << b3 << endl;
    }
};
class B4
{
private:
    int b4;
public:
    B4(int i)
    {
        b4 = i;
        cout << "construcor B4.b4=" << b4 << endl;
    }
    void Print()
    {
        cout << b4 << endl;
    }
    ~B4()
    {
        cout << "destructor B4.b4=" << b4 << endl;
    }
};
class D :public B3, public B1, public B4
{
private:
    int d;
    B2 b2;
public:
    D(int d1, int d2, int d3, int d4, int d5) :B1(d1), B3(d3), B4(d4), b2(d5)
    {
        d = d2;
    }
    void Print()
    {
        B1::Print();
        cout << d << endl;
        B3::Print();
        B4::Print();
        b2.Print();
    }
};
int main()
{
    D d(11, 12, 13, 14, 15);
    d.Print();
    return 0;
}

多重继承的二义性:

(1).调用不同基类的相同成员时可能出现二义性

class A
{
public:
    void f()

};

class B
{
public:
    void f();
    void g();
};

class C:public A,public B
{
public:
    void g();
    void h()
    {
        A::f();
    }
};

C c;
c.f();//不知道调用哪一个f(),因为A和B中各有一个f().
c.A::f();
c.B::f();
c.g(); //不存在二义性

(2).访问共同基类的成员时可能出现二义性

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;
class A
{
private:
    int a;
public:
    A(int i)
    {
        a = i;
        cout << "constructor called!A\n";
    }
    void Print()
    {
        cout << a <<" ";
    }
    ~A()
    {
        cout << "destructor called.A\n";
    }
};
class B1 :public A
{
private:
    int b1;
public:
    B1(int i, int j) :A(i)
    {
        b1 = j;
        cout << "constructor callled B1\n";
    }
    void Print()
    {
        A::Print();
        cout << b1 << endl;
    }
    ~B1()
    {
        cout << "destructor called.B1\n";
    }

};
class B2 :public A
{
private:
    int b2;
public:
    B2(int i, int j) :A(i)
    {
        b2 = j;
        cout << "constructor callled B2\n";
    }
    void Print()
    {
        A::Print();
        cout << b2 <<endl;
    }
    ~B2()
    {
        cout << "destructor called.B2\n";
    }

};
class C :public B2, public B1
{
private:
    int c;
public:
    C(int i, int j, int k, int l, int m) :B1(i, j), B2(k, l)
    {
        c = m;
        cout << "constructor called C\n";
    }
    void Print()
    {
        B1::Print();
        B2::Print();
        cout << c << endl;
    }
    ~C()
    {
        cout << "destructor called.C\n";
    }

};
int main()
{
    C c(1, 2, 3, 4, 5);
    c.Print();
    c.B1::A::Print();

    return 0;
}

虚基类和虚继承

如前上所述,派生了C中包含了2个类A的成员,即出现了公共基类
不仅给程序带来二义性,而且还会在创建C类对象的时候2次调用A 的构造函数.
为了解决这个问题,避免可能出现的二义性和使用公共基类值产生一个实例,需要把这个公共基类声明为虚拟的(virtual)基类,简称虚基类
(1).虚基类的说明方法:

class A
{...}

class B:virtual public A
{}

class C:virual public A
{}

class D:public B,public C
{}

这里,类A被说明为虚基类
#include <iostream>
#pragma  warning(disable:4996)
using namespace std;
class A
{
private:
    int a;
public:
    A(int i)
    {
        a = i;
        cout << "constructor called!A\n";
    }
    void Print()
    {
        cout << a <<" ";
    }
    ~A()
    {
        cout << "destructor called.A\n";
    }
};
class B1 :virtual public A
{
private:
    int b1;
public:
    B1(int i, int j) :A(i)
    {
        b1 = j;
        cout << "constructor callled B1\n";
    }
    void Print()
    {
        A::Print();
        cout << b1 << endl;
    }
    ~B1()
    {
        cout << "destructor called.B1\n";
    }

};
class B2 :virtual public A
{
private:
    int b2;
public:
    B2(int i, int j) :A(i)
    {
        b2 = j;
        cout << "constructor callled B2\n";
    }
    void Print()
    {
        A::Print();
        cout << b2 <<endl;
    }
    ~B2()
    {
        cout << "destructor called.B2\n";
    }

};
class C :public B2, public B1
{
private:
    int c;
public:
    C(int i, int j, int k, int l, int m) :B1(i, j), B2(k, l),A(i)
    {
        c = m;
        cout << "constructor called C\n";
    }
    void Print()
    {
        B1::Print();
        B2::Print();
        cout << c << endl;
    }
    ~C()
    {
        cout << "destructor called.C\n";
    }

};
int main()
{
    C c(1, 2, 3, 4, 5);
    c.Print();
    c.B1::A::Print();


    return 0;
}

(2).虚基类的派生类构造函数
为了实现对公共基类只创建一次,则需要在派生类的构造函数的成员成员初始化列表中列出虚基类的构造函数.格式如下:
派生类构造函数名(参数表):若干个直接基类的构造函数,子对象类的构造函数,虚基类构造函数
{
派生类构造函数体
}
C++规定:
在派生类构造函数的初始化列表中出现的虚基类构造函数优先于非虚基类构造函数的调用
,只在创建对象的派生类构造函数中调用虚基类构造函数,而派生类的基类构造函数中不再调用虚基类的构造函数,这样就保证了对虚基类只初始化一次.

#include <iostream>
#pragma  warning(disable:4996)
using namespace std;
class A
{
private:
    int a;
public:
    A(int i)
    {
        a = i;
        cout << "constructor called!A\n";
    }
    void Print()
    {
        cout << a <<" ";
    }
    ~A()
    {
        cout << "destructor called.A\n";
    }
};
class B1 :virtual public A
{
private:
    int b1;
public:
    B1(int i, int j) :A(i)
    {
        b1 = j;
        cout << "constructor callled B1\n";
    }
    void Print()
    {
        A::Print();
        cout << b1 << endl;
    }
    ~B1()
    {
        cout << "destructor called.B1\n";
    }

};
class B2 :virtual public A
{
private:
    int b2;
public:
    B2(int i, int j) :A(i)
    {
        b2 = j;
        cout << "constructor callled B2\n";
    }
    void Print()
    {
        A::Print();
        cout << b2 <<endl;
    }
    ~B2()
    {
        cout << "destructor called.B2\n";
    }

};
class C :public B2, public B1
{
private:
    int c;
public:
    C(int i, int j, int k, int l, int m) :B1(i, j), B2(k, l),A(i)
    {
        c = m;
        cout << "constructor called C\n";
    }
    void Print()
    {
        B1::Print();
        B2::Print();
        cout << c << endl;
    }
    ~C()
    {
        cout << "destructor called.C\n";
    }
};
int main()
{
    C c(1, 2, 3, 4, 5);
    /*c.Print();
    c.B1::A::Print();*/
    cout << sizeof(c) << endl;  //  24       //20

    return 0;
}

其他

杂例:

#include <iostream>
using namespace std;

class A
{
public:
    A()
    {
        cout << "A construcot called!" << endl;
    }
    void Print_A()
    {
        cout << "A" << endl;
    }
    void Func();
};

class B :public A
{
public:
    B()
    {
        cout << "B constructor called" << endl;
    }
    void Print_B()
    {
        cout << "B" << endl;
    }
};

void A::Func()
{
    B b;
    b.Print_B();
    B();
}

int main()
{
    A a;
    a.Func();
    return 0;
}