Cpp多重继承会产生的问题

多重继承常常被认为是 OOP 中一种复杂且不必要的部分。多重继承面临 crash 的场景并非难以想象,来看下面的例子。

1. 名称冲突

来看以下情况:

如果 Dog 类以及 Bird 类都有一个名为 eat() 的方法,而子类又没有 override 该方法。如果此时调用子类的 eat() 方法,编译器就会报错,指出 eat() 的调用有歧义(不知道是调用从 Dog 类继承而来的 eat() 方法,还是从 Bird 类继承而来的 eat() 方法)。代码如下:

class Dog
{
public:
    virtual void eat() {};
};

class Bird
{
public:
    virtual void eat() {};
};

class DogBird : public Dog, public Bird
{

};

int main()
{
    DogBird db;
    db.eat(); // BUG! Ambiguous call to method eat()
    return 0;
}

/*
编译错误:
[[email protected] AAA]$ clang++ aa.cc
aa.cc:21:8: error: member ‘eat‘ found in multiple base classes of different types
    db.eat(); // BUG! Ambiguous call to method eat()
       ^
aa.cc:4:18: note: member found by ambiguous name lookup
    virtual void eat() {};
                 ^
aa.cc:10:18: note: member found by ambiguous name lookup
    virtual void eat() {};
                 ^
1 error generated.
*/

解决方法:

#include <iostream>
using namespace std;

class Dog
{
public:
    virtual void eat() {cout << "The Dog has eaten." << endl;};
};

class Bird
{
public:
    virtual void eat() {cout << "The Bird has eaten." << endl;};
};

class DogBird : public Dog, public Bird
{

};

int main()
{
    DogBird db;
    static_cast<Dog>(db).eat(); // Slices, calling Dog::eat()
    db.Bird::eat();             // Calls Bird::eat()
    return 0;
}

/*
Output:
The Dog has eaten.
The Bird has eaten.
*/

为了消除歧义,要么在 DogBird类重写 eat() 方法,要么显示的指明调用的是哪一个父类的版本。

2. 歧义基类

来看以下情况:

虽然可能产生 name ambiguity,但是 C++ 允许这种类型的类层次结构。例如,如果 Animal 类有一个 public 方法 sleep(),那么 DogBird 对象将无法调用这个方法,因为编译器不知道调用 Dog 继承的版本还是 Bird 继承的版本。代码如下:

class Animal
{
public:
    void sleep(){}
};

class Dog : public Animal
{
};

class Bird : public Animal
{
};

class DogBird : public Dog, public Bird
{
};

int main()
{
    DogBird db;
    db.sleep();
    return 0;
}

/*
发生编译错误

[[email protected] ~]$ clang++ aa.cc
aa.cc:25:8: error: non-static member ‘sleep‘ found in multiple base-class subobjects of type ‘Animal‘:
    class DogBird -> class Dog -> class Animal
    class DogBird -> class Bird -> class Animal
    db.sleep();
       ^
aa.cc:7:10: note: member found by ambiguous name lookup
    void sleep(){}
         ^
1 error generated.
*/

使用“菱形”类层次结构的最佳方法是将最顶部的类设置为抽象类,所有方法都设置为纯虚方法。由于类只声明方法而不提供定义,在基类中没有方法可以调用,因此在这个层次上就不会产生歧义。代码如下:

#include <iostream>
using namespace std;

class Animal
{
public:
    virtual void sleep() = 0;
};

class Dog : public Animal
{
public:
    virtual void sleep()
    {
        cout << "Dog sleep!" << endl;
    }
};

class Bird : public Animal
{
public:
    virtual void sleep()
    {
        cout << "Bird sleep!" << endl;
    }
};

class DogBird : public Dog, public Bird
{
public:
    // 注意:虽然从语法上来说,DogBird可以不override sleep方法
    // 但是如此一来,再调用DogBird类的sleep方法时,会分不清是Dog类的还是Bird类的
    virtual void sleep()
    {
        cout << "DogBird sleep!" << endl;
    }
};

int main()
{
    DogBird db;
    db.sleep();
    return 0;
}

/*
Output:
DogBird sleep!
*/

小结

我们往往会在定义一个“既是一个事物同时又是另外一个事物”的情况下使用多重继承,然而,实际上遵循这个模式的实际对象很难恰如其分的转换为合适的代码,因此在工程中,我们要尽量避免使用多重继承。

时间: 2024-08-14 15:40:48

Cpp多重继承会产生的问题的相关文章

不可或缺 Windows Native (22) - C++: 多重继承, 虚基类

[源码下载] 作者:webabcd 介绍不可或缺 Windows Native 之 C++ 多重继承 虚基类 示例1.基类 1CppBase1.h #pragma once #include <string> #include "CppBaseVirtual.h" using namespace std; namespace NativeDll { // virtual 代表 CppBaseVirtual 是 CppBase1 的虚基类(虚基类是在声明派生类时,指定继承方式

多重继承,虚继承,MI继承中虚继承中构造函数的调用情况

先来测试一些普通的多重继承.其实这个是显而易见的. 测试代码: [cpp] view plain copy print? //测试多重继承中派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include <iostream> using namespace std; class base { public: base() { cout<<"base created!"<<endl; } ~base()

【ThinkingInC++】75、多重继承

第九章 多重继承 9.2 接口继承 Intertfacees.cpp /** * 书本:[ThinkingInC++] * 功能:接口继承Interfaces.cpp * 时间:2014年10月28日20:06:31 * 作者:cutter_point */ #include <iostream> #include <sstream> #include <string> using namespace std; class Printable //抽象类 { publi

CPP 学习笔记随笔

CPP对象模型 何为C++对象模型? C++对象模型可以概括为以下2部分: 语言中直接支持面向对象程序设计的部分 对于各种支持的底层实现机制 语言中直接支持面向对象程序设计的部分,如构造函数.析构函数.虚函数.继承(单继承.多继承.虚继承).多态等等,这也是组里其他同学之前分享过的内容.第一部分这里我简单过一下,重点在底层实现机制. 在c语言中,"数据"和"处理数据的操作(函数)"是分开来声明的,也就是说,语言本身并没有支持"数据和函数"之间的关

阅读程序 多重继承

/* *Copyright (c)2014,烟台大学计算机与控制工程学院 *All rights reserved. *文件名称:d.cpp *作 者:张旺华 *完成日期:2015年6月1日 *版 本 号:v1.0 */ #include <iostream> using namespace std; class A { public: A(char *s) { cout<<s<<endl; } }; class B:public A { public: B(char

cl.exe命令方式编译cpp

直接在命令行窗口调用cl编译cpp文件 往往不能通过. 主要原因是一些头文件及可执行文件未在环境变量中设置.可以通过执行VSVAR32.BAT批处理文件来设置环境变量,注意vs2005跟2008的放置位置可能不同,如果不在cl.exe当前路径下,可以去/Common7/Tools目录下寻找. 批处理文件中主要是set命令临时配置的环境变量,所以开机之后需要重新运行该bat文件才能执行cl命令. 另附cl命令使用说明 CL.exe 是控制 Microsoft C 和 C++ 编译器与链接器的 32

Effective C++:条款40:明智而审慎地使用多重继承

(一) 慎用多重继承,因为那样的话可能会造成歧义.. <pre name="code" class="cpp">class BorrowableItem { public: void checkOut(); }; class ElectronicGadet { private: bool checkOut() const; }; class MP3Player : public BorrowableItem <pre name="code

《C++ Primer Plus》14.3 多重继承 学习笔记

多重继承(MI)描述的是有多个直接基类的类.与单继承一样,共有MI表示的也是is-a关系.例如,可以从Awiter类和Singer类派生出SingingWaiter类:class SingingWaiter : public Waiter, public Singer {...};MI可能会给程序员带来很多新问题.其中两个主要的问题是:从两个不同的基类继承同名方法:从两个或更多相关基类那里继承同一个类的多个实例.在下面的例子中,我们将定义一个抽象基类Worker,并使用它派生出Waiter类和S

多重继承的注意点

1, 钻石型多重继承如果不想要底部的类有重复的变量,则需要声明为virtual继承 class File{...}; class InputFile: virtual public File{..}; class OutputFile: virtual public File{....}; class IOFile: public InputFile, public OutputFile {...}; 2, 多重继承来的成员函数指向一边的指针不能访问另一边的函数 ////////////////