您的位置:首页 > 编程语言 > C语言/C++

Cpp多重继承会产生的问题

2015-04-06 15:35 274 查看
多重继承常常被认为是 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;
}

/*
编译错误:
[purple@archlinux 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;
}

/*
发生编译错误

[purple@archlinux ~]$ 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!
*/


小结

我们往往会在定义一个“既是一个事物同时又是另外一个事物”的情况下使用多重继承,然而,实际上遵循这个模式的实际对象很难恰如其分的转换为合适的代码,因此在工程中,我们要尽量避免使用多重继承。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: