欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

c++ primer plus学习笔记(7)——类继承

程序员文章站 2022-03-09 09:23:12
...

 2.1多继承

在这一节,我们将围绕构造函数的发生、方法的二义性进行讨论

2.1.1构造、析构顺序:栈原则

假如一个test类继承两个基类:base1、base2,下述代码显示了他们的构造顺序。

#include <iostream>
using namespace std;
class Base1
{
public:
    Base1() { cout << "Base1 constructor\n"; };
    ~Base1() { cout << "Base1 destructor\n"; };
};

class Base2
{
public:
    Base2() { cout << "Base2 constructor\n"; };
    ~Base2() { cout << "Base2 destructor\n"; };
};

class Test : public Base1, public Base2
{
public:
    Test() : Base1(), Base2() { cout << "Test constructor\n"; };
    ~Test() { cout << "Test destructor\n"; };
};

int main()
{
    Test a;
}

输出:

Base1 constructor
Base2 constructor
Test constructor
Test destructor
Base2 destructor
Base1 destructor


如果将test的构造函数这样定义,即先写base2,再写base1,输入仍然不变。这说明基类的构造函数是按照声明顺序调用的。

class Test : public Base1, public Base2
{
public:
    Test() : Base2(), Base1() { cout << "Test constructor\n"; };
    ……
}

复杂的例:一个worker类分别派生出两个类:singer类、waiter类,以表示歌手和侍者的信息。这两个类又派生出一个类:SingerWaiter类。

#include <iostream>
using namespace std;
class Worker
{
public:
    Worker() {cout << "Worker constructor\n"; };
    ~Worker() {cout << "Worker destructor\n"; };
    void show() { cout << "show called\n"; };
};

class Singer : public Worker
{
public:
    Singer() { cout << "Singer constructor\n"; };
    ~Singer() { cout << "Singer destructor\n"; };
};

class Waiter : public Worker
{
public:
    Waiter() { cout << "Waiter constructor\n"; };
    ~Waiter() { cout << "Waiter destructor\n"; };
};

class SingerWaiter : public Singer, public Waiter
{
public:
    SingerWaiter() : Singer(), Waiter() { cout << "SingerWaiter constructor\n"; };
    ~SingerWaiter() { cout << "SingerWaiter destructor\n"; };
};

int main()
{
    SingerWaiter x;
}

输出:

Worker constructor
Singer constructor
Worker constructor
Waiter constructor
SingerWaiter constructor
SingerWaiter destructor
Waiter destructor
Worker destructor
Singer destructor
Worker destructor


2.1.2方法二义性

如前述复杂的例,若调用show方法,将调用哪个worker类的show方法?

int main()
{
    SingerWaiter x;
    x.show();
}

编译器报错:

test.cpp: In function 'int main()':
test.cpp:59:7: error: request for member 'show' is ambiguous
     x.show();


解决方法:使用作用域运算符,如下述代码将调用singer类的show方法

int main()
{
    SingerWaiter x;
    x.Singer::show();
}

2.2虚继承

一个典型的问题:我们为什么需要两个worker类?

为了解决这个问题,c++引入虚继承。在继承列表使用关键字virtual,使得继承变成虚继承,其基类为虚基类。

2.2.1构造函数、析构函数

  • 虚继承的类被多重继承时,其虚基类是共享的。
  • c++禁止信息通过中间类传递给虚基类,我们需要直接使用虚基类的构造函数。如:
#include <iostream>
using namespace std;
class Worker
{
public:
    Worker() { cout << "Worker constructor\n"; };
    ~Worker() { cout << "Worker destructor\n"; };
    void show() { cout << "show called\n"; };
};

class Singer : virtual public Worker
{
public:
    Singer() { cout << "Singer constructor\n"; };
    ~Singer() { cout << "Singer destructor\n"; };
};

class Waiter : virtual public Worker
{
public:
    Waiter() { cout << "Waiter constructor\n"; };
    ~Waiter() { cout << "Waiter destructor\n"; };
};

class SingerWaiter : public Singer, public Waiter
{
public:
    SingerWaiter() : Worker(), Singer(), Waiter() 
    { cout << "SingerWaiter constructor\n"; };
    ~SingerWaiter() { cout << "SingerWaiter destructor\n"; };
};

int main()
{
    SingerWaiter x;
    x.show();
}

输出:

Worker constructor
Singer constructor
Waiter constructor
SingerWaiter constructor
show called
SingerWaiter destructor
Waiter destructor
Singer destructor
Worker destructor


worker只被构造一次,这表明singer和waiter共同使用一个worker类。

2.2.2二义性

一个典型的问题:上述输出中哪个show被调用?

如果没有重定义show方法,虚继承将不产生问题,解决了普通继承的缺点。但是如下例,若在中间类重新定义了show方法:

#include <iostream>
using namespace std;
class Worker
{
public:
    void show() { cout << "show called\n"; };
};

class Singer : virtual public Worker
{
public:
    void show() { cout << "show1 called\n"; };
};

class Waiter : virtual public Worker
{
public:
    void show() { cout << "show2 called\n"; };
};

class SingerWaiter : public Singer, public Waiter {……};

int main()
{
    SingerWaiter x;
    x.show();
}

编译器报错:

test.cpp: In function 'int main()':
test.cpp:37:7: error: request for member 'show' is ambiguous
     x.show();


修复方法1:使用作用域运算符

int main()
{
    SingerWaiter x;
    x.Singer::show();
}

在最近处(SingerWaiter)重定义show方法

class SingerWaiter : public Singer, public Waiter
{
public:
    void show() { cout << "show3 called\n"; };
};

int main()
{
    SingerWaiter x;
    x.show();
}

2.3方法匹配:最近原则

多重继承时,若直接基类有重名函数,

  • 普通继承一定引发二义性;
  • 虚继承不一定引发二义性。其匹配原则是派生类优先于基类或间接基类

如:若只在Singer类中重声明show方法,则Singer类中的show方法优先于Worker中的,不产生二义性。

#include <iostream>
using namespace std;
class Worker
{
public:
    void show() { cout << "Worker called\n"; };
};

class Singer : virtual public Worker
{
public:
    void show() { cout << "Singer called\n"; };
};

class Waiter : virtual public Worker {……};

class SingerWaiter : public Singer, public Waiter {……};

int main()
{
    SingerWaiter x;
    x.show();
}

2.4混合使用虚继承与普通继承

如下述代码,Base虚派生出D1,D2,派生出D3,M继承这三个中间类。则M中有两个Bae类D1、D2的共同Bas类,和D3中包含的Base类。

#include <iostream>
using namespace std;
class Base
{
public:
    Base() { cout << "Base constructor\n"; };
    ~Base() { cout << "Base destructor\n"; };
};

class D1 : virtual public Base
{
public:
    D1() { cout << "D1 constructor\n"; };
    ~D1() { cout << "D1 destructor\n"; };
};

class D2 : virtual public Base
{
public:
    D2() { cout << "D2 constructor\n"; };
    ~D2() { cout << "D2 destructor\n"; };
};

class D3 : public Base
{
public:
    D3() { cout << "D3 constructor\n"; };
    ~D3() { cout << "D3 destructor\n"; };
};

class M : public D1, public D2, public D3
{
public:
    M() : Base(), D1(), D2(), D3() { cout << "M constructor\n"; };
    ~M() { cout << "M destructor\n"; };
};

int main()
{
    M x;
}

输出:

Base constructor
D1 constructor
D2 constructor
Base constructor
D3 constructor
M constructor
M destructor
D3 destructor
Base destructor
D2 destructor
D1 destructor
Base destructor


3.类模板