简体   繁体   English

我如何控制继承代码的不同部分

[英]How can i accsess different parts of my inherited code

Hi i have a question regarding how to access parts of inherited code. 嗨,我有一个关于如何访问继承代码部分的问题。

Say i have this WorldObject that is a base class for alot of other objects. 说我有这个WorldObject是许多其他对象的基类。 Then i have a class Chest that inherit from WorldObject and also from the abstract class OpenAble, with some methods like open and unlock. 然后,我有一个Chest类,它继承自WorldObject和抽象类OpenAble,并带有一些方法,如打开和解锁。

In my main i have a vector of WorldObjects that i iterate through with a for loop. 在我的主要语言中,我有一个WorldObjects向量,可以使用for循环进行迭代。 Now to the question, how can i check if a worldobject is also of OpenAble and how can i access the methods in OpenAble. 现在要问的问题是,如何检查worldobject是否也是OpenAble的,以及如何访问OpenAble中的方法。

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?
}
};

You could do a dynamic_cast<OpenAble> . 您可以执行dynamic_cast<OpenAble> This will throw an error if it is the wrong type though which is relatively expensive given that it is quite likely that the object will be the wrong type. 如果类型错误,这将引发错误,但是由于对象很可能是错误的类型,所以这是相对昂贵的。

try{
  OpenAble& opener = dynamic_cast<OpenAble&>(worldObj);
} catch (std::bad_cast& ex){
  //not openable
}

BTW: As pointed out in the comments below, if you use a pointer to the base class in your container instead of references, then you can (and should) use the pointer version of dynamic_cast which will return a null in the case that your object is not OpenAble. 顺便说一句:正如下面的注释所指出的,如果您在容器中使用指向基类的指针而不是引用,则可以(并且应该)使用dynamic_cast的指针版本,如果您的对象使用该指针版本,则将返回null不是OpenAble。 Checking that in your case would be a lot more efficient than throwing and catching exceptions. 在您的情况下进行检查比抛出和捕获异常要有效得多。

I would recommend an entirely different approach though. 我会建议一种完全不同的方法。 Inject your base class with an "OpenPolicy". 为您的基类注入“ OpenPolicy”。

Eg 例如

class CanOpenPolicy {
public:
  boolean canOpen(){ return true; };
  boolean canClose(){ return true; };
  boolean isOpen(){ return openState; };
  void open(){ openState = OPEN; };
  void close(){ openState = CLOSED; };
}

class NoOpenPolicy {
public:
  boolean canOpen(){ return false; };
  boolean canClose(){ return false; };
  boolean isOpen(){ return CLOSED; };
  void open(){ throw IllegalWorldObjectAction("OpenPolicy disallows operation"); };
  void close(){ throw IllegalWorldObjectAction("OpenPolicy disallows operation"); };
}

//injection via template (no need for base "OpenPolicy" class, maybe some
// obscure error codes at compile though)
// Implicit interface based on how you use the injected policy.
template<OpenPol>
class WorldObject {
private: 
  // CTOR part of the injected contract so you are not tied to knowing how to 
  // build the policy. This is a key benefit over interface based injection.
  OpenPol openPol; 
  ...
public:
  ...
  void open(){
    if(openPol.canOpen()){
      openPol.open();
    }
  }
  ...
}

That's not tested or anything. 这未经测试或任何东西。 Just to illustrate the idea. 只是为了说明这个想法。 You can add multiple policies for different possible operations and the best thing is that you won't need a lot of hierarchies. 您可以为不同的可能操作添加多个策略,最好的事情是您不需要太多的层次结构。

To use it just do something like this: 要使用它,只需执行以下操作:

std::unique_ptr<WorldObject>( new Chest() );
std::unique_ptr<WorldObject>( new Banana() );
std::unique_ptr<WorldObject>( new Chair() );

where: 哪里:

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> {
}

The most important thing, even though you may not like this, is to not throw away type information in the first place. 即使您可能不喜欢,最重要的是不要一开始就丢弃类型信息。

Collections of generic "object" is a Java'ism, it's not how to do things in C++. 通用“对象”的集合是Java主义,不是用C ++来做事。

That said, provided the statically known class is polymorphic (has at least one virtual member function), you can use dynamic_cast or typeid . 也就是说,如果静态已知的类是多态的(具有至少一个虚拟成员函数),则可以使用dynamic_casttypeid This functionality is known as RTTI , short for Run Time Type Information . 此功能称为RTTI ,是运行时类型信息的简称。 With some compilers you have to use special options to enable RTTI. 对于某些编译器,您必须使用特殊选项来启用RTTI。

Idiomatic use of dynamic_cast : 习惯用法dynamic_cast

WorldObject* p = ...;
if( auto p_openable = dynamic_cast<OpenAble*>( p ) )
{
    // use p_openable
}

Note that dynamic_cast to pointer signals failure by returning a nullpointer, while dynamic_cast to reference signals failure by throwing an exception, since there are no nullreferences. 请注意,指向动态指针的dynamic_cast通过返回空指针来指示失败,而指向动态指针的dynamic_cast通过抛出异常来指示失败,因为没有空引用。

The simple (obvious) solution is to use dynamic_cast and cast your objects to OpenAble . 一种简单(显而易见)的解决方案是使用dynamic_cast并将您的对象转换为OpenAble

The problem with "the simple (obvious) solution" is that usually, use of dynamic_cast shows a lack of flexibility in your class hierarchy and is a symptom of a design problem. “简单(显而易见)的解决方案”的问题在于,通常,dynamic_cast的使用表明您的类层次结构缺乏灵活性,这是设计问题的征兆。

I would offer the OpenAble interface as a set of behavior exposed through a handle: 我将提供OpenAble接口作为通过句柄公开的一组行为:

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();
    }
};

Client code: 客户代码:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何编写将被继承的通用代码? - How can I write generic code that will be inherited? 在 C++ 中,我如何将我的 arguments 继承到另一个 class 中? - In C++ how can I inherited my arguments into another class? 我如何知道代码中的哪些部分从未使用过? - How can I know which parts in the code are never used? 我如何使我的代码与众不同以删除时间限制? - How can i make my code different to remove the time limit? 我可以使用CMake以不同的警告级别编译项目的不同部分吗? - Can I compile different parts of my project with different warning levels with CMake? C ++我有一个类的数组,我想要它的元素 - C++ I have an array of my class and i want to accsess elements of it TBB:如何在代码的不同部分设置不同数量的线程? - TBB: How to set different number of threads in different parts of code? (Visual Studio)如何知道我的c ++程序中哪些部分已运行,哪些未运行? - (Visual Studio) How can I tell which parts of my c++ program ran and which didn't? 我在注册表中的哪里可以找到音频设备名称的不同部分 - where in registry can i find different parts of audio device name 如何使用不同的浮点选项编译部分代码? - How to compile parts of code with different floating-point options?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM