C++ 如何访问继承代码的不同部分

C++ 如何访问继承代码的不同部分,c++,inheritance,multiple-inheritance,C++,Inheritance,Multiple Inheritance,您好,我有一个关于如何访问部分继承代码的问题 假设我有一个WorldObject,它是许多其他对象的基类。然后我有一个类cast,它继承自WorldObject,也继承自抽象类OpenAble,使用了一些方法,如open和unlock 在我的主要部分中,我有一个WorldObjects的向量,我用for循环遍历它。现在我要问的问题是,如何检查worldobject是否也是可打开的,以及如何访问可打开中的方法 class WorldObject { ... //implementation

您好,我有一个关于如何访问部分继承代码的问题

假设我有一个WorldObject,它是许多其他对象的基类。然后我有一个类cast,它继承自WorldObject,也继承自抽象类OpenAble,使用了一些方法,如open和unlock

在我的主要部分中,我有一个WorldObjects的向量,我用for循环遍历它。现在我要问的问题是,如何检查worldobject是否也是可打开的,以及如何访问可打开中的方法

class WorldObject
{
...     //implementation
};

class OpenAble
{
public:
    OpenAble(){}
    virtual ~OpenAble(){}
    virtual void Open() = 0;
    virtual void Unlock(int k) = 0;
};

class Chest : public WorldObject, public OpenAble
{
...  //implementation
};

main()
{
std::vector<WorldObject> objVector;     //vector with several Worldobjects

for (int i =0; i < objVector.Size(); i++)
{
//check if a WorldObject is also of openable
//Do som actions like, open or unlock
//How?
}
};
class世界对象
{
…//实现
};
类可打开
{
公众:
可打开(){}
虚拟~OpenAble(){}
虚空打开()=0;
虚空解锁(int k)=0;
};
类箱:公共WorldObject,可公开
{
…//实现
};
main()
{
std::vector objVector;//包含多个世界对象的向量
对于(int i=0;i
您可以进行
动态播放
。如果对象是错误的类型,这将抛出一个错误,尽管这是相对昂贵的,因为对象很可能是错误的类型

try{
  OpenAble& opener = dynamic_cast<OpenAble&>(worldObj);
} catch (std::bad_cast& ex){
  //not openable
}
试试看{
可开启和开启器=动态投影(worldObj);
}捕捉(标准:错误的施法和ex){
//无法打开
}
顺便说一句:正如下面的评论所指出的,如果您在容器中使用指向基类的指针而不是引用,那么您可以(而且应该)使用dynamic_cast的指针版本,如果您的对象不可打开,它将返回null。在您的案例中,检查这一点比抛出和捕获异常要有效得多

不过,我会推荐一种完全不同的方法。用“OpenPolicy”注入基类

例如

类策略{
公众:
布尔canOpen(){return true;};
布尔canClose(){return true;};
布尔isOpen(){return openState;};
void open(){openState=open;};
void close(){openState=CLOSED;};
}
阶级政策{
公众:
布尔canOpen(){return false;};
布尔canClose(){return false;};
布尔isOpen(){return CLOSED;};
void open(){throw IllegalWorldObjectAction(“OpenPolicy不允许操作”);};
void close(){throw IllegalWorldObjectAction(“OpenPolicy不允许操作”);};
}
//通过模板注入(不需要基本的“OpenPolicy”类,可能需要一些
//在编译时隐藏错误代码(尽管如此)
//基于如何使用注入策略的隐式接口。
模板
类世界对象{
私人:
//CTOR是注入合同的一部分,因此您不必知道如何
//构建策略。这是基于接口的注入的一个关键优势。
OpenPol OpenPol;
...
公众:
...
空开(){
if(openPol.canOpen()){
openPol.open();
}
}
...
}
那没有经过测试或其他什么。只是为了说明这个想法。您可以为不同的可能操作添加多个策略,最好的是不需要太多层次结构

要使用它,只需执行以下操作:

std::unique_ptr<WorldObject>( new Chest() );
std::unique_ptr<WorldObject>( new Banana() );
std::unique_ptr<WorldObject>( new Chair() );
std::unique_ptr(新胸部());
std::unique_ptr(新香蕉());
标准::独特的ptr(新椅子());
其中:

class Chest : public WorldObject<CanOpenPolicy> {
   // Very little implementation in here.
   // Most of it is handled in the base class and the injected policies :)
}
class Banana: public WorldObject<CanOpenPolicy> {
}
class Chair : public WorldObject<NoOpenPolicy> {
}
类箱:公共WorldObject{
//这里几乎没有实现。
//大部分在基类和注入的策略中处理:)
}
类:公共WorldObject{
}
班长:公共WorldObject{
}

最重要的一点是,即使您可能不喜欢这样做,首先也不要丢弃类型信息

通用的“对象”集合是Java'ISM,不是C++中如何做的事情。 也就是说,如果静态已知类是多态的(至少有一个虚拟成员函数),则可以使用

dynamic\u cast
typeid
。此功能称为RTTI,是运行时类型信息的缩写。对于某些编译器,您必须使用特殊选项来启用RTTI

dynamic\u cast
的惯用用法:

WorldObject* p = ...;
if( auto p_openable = dynamic_cast<OpenAble*>( p ) )
{
    // use p_openable
}
WorldObject*p=。。。;
if(自动p_可打开=动态p_铸造)
{
//使用p_openable
}
请注意,
dynamic\u cast
to pointer通过返回空指针表示失败,而
dynamic\u cast
to reference通过抛出异常表示失败,因为没有空引用。

简单(明显)的解决方案是使用dynamic\u cast并将对象强制转换为可打开的
OpenAble

“简单(明显)解决方案”的问题是,通常,使用dynamic_cast表明类层次结构缺乏灵活性,这是设计问题的一个症状

我将提供可打开的接口,作为通过句柄公开的一组行为:

class OpenAble { /* ... */ };

class WorldObject
{
    //implementation
    virtual OpenAble* GetOpener() { return nullptr; }
};

class Chest: public WorldObject {
    struct ChestOpener: public OpenAble {
         Chest *c;
         virtual void Open() {
             // do stuff with c
         }
    };
    std::unique_ptr<OpenAble> chest_opener;
public:
    virtual OpenAble* GetOpener() {
        if(!chest_opener) {
             chest_opener = new ChestOpener{ this };
        }
        return chest_opener.get();
    }
};
类可打开{/*…*/};
类世界对象
{
//实施
虚拟可打开*GetOpener(){return nullptr;}
};
类别:公共世界对象{
结构ChestOpener:可公开{
胸部*c;
虚拟空打开(){
//用c做东西
}
};
std::独特的开胸器;
公众:
虚拟可打开*GetOpener(){
如果(!开胸器){
开胸器=新开胸器{this};
}
返回开胸器。获取();
}
};
客户端代码:

std::vector<WorldObject> objVector;     //vector with several Worldobjects

for(auto &obj: objVector)
{
    if(auto openerHandle = obj.GetOpener())
        openerHandle->Open();
}
std::vector objVector//具有多个世界对象的向量
用于(自动和对象:对象向量)
{
if(auto openerHandle=obj.GetOpener())
openerHandle->Open();
}

请注意,dynamic_cast需要RTTI;另请参见和接受的答案。@Dennis,如果不是
dynamic\u castOpenAble&>(…)
,则强制转换必须指向引用或指针。(一个指针转换,通过检查null来判断它是否成功,可能比本地处理的异常更好,无论是在效率还是总体外观方面。)是的,使用指针和检查会更好,但是OP使用的是引用,所以我想我应该这样做
std::vector<WorldObject> objVector;     //vector with several Worldobjects

for(auto &obj: objVector)
{
    if(auto openerHandle = obj.GetOpener())
        openerHandle->Open();
}