C ++抽象基类问题

时间:2009-12-19 07:23:44

标签: c++

所以我在C ++中遇到了ABC设计的问题。我将使用我实际拥有的简化示例。在我遇到问题之前,请忽略任何语法错误。

所以我有一个ABC:

class ABC  
{  
public:  
    virtual void DoSomething() = 0;  
};

然后我有一个派生类:

class Derived: public ABC  
{  
public:  
    void DoSomething() { // something }  
};

在我的主要逻辑功能中,我有以下几点:

ABC* obj = new Derived;
obj->DoSomething();

现在,在这一点上,我的代码运行得很漂亮,如果我使用抽象基类作为派生对象的类型,我将类型(现在手动)从一个派生类更改为另一个派生类,我有多个类正常运行。 / p>

现在我的问题......

如果我想更改我的派生类以添加ABC不支持的功能,那么我的编译器无法识别它们。例如,如果我按原样离开ABC并将Derived更改为:

class Derived: public ABC  
{  
public:  
    void DoSomething() { // something }  
    void DoSomethingElse() { // something else, not defined in ABC}  
}; 

然后回到我的主要逻辑功能:

ABC* obj = new Derived;  
obj->DoSomething(); 
// Compiler does not recognize this, due to not being in the "ABC" class   
obj->DoSomethingElse(); 

我的编译器一直给我错误“DoSomethingElse()”不是ABC的成员。我正在尝试做什么?感觉就像我忽略了一些简单的事情,但我在这个软件的架构中遇到了这个障碍。非常感谢任何帮助。

6 个答案:

答案 0 :(得分:9)

您正在尝试调用派生类中仅存在 的方法,因此您需要像这样强制转换obj指针来调用此方法:

Derived* derivedObj = dynamic_cast<Derived*>(obj);
derivedObj->DoSomethingElse();

请注意,dynamic_cast将执行运行时检查以确保obj是同一类层次结构的一部分(即它是“有效”强制转换),并且如果强制转换将返回0是无效的。请注意,您需要启用RTTI才能使用dynamic_cast

像这样的演员可以被视为代码嗅觉,并且可能有更好的方法来实现你正在做的事情。

当您使用抽象基类时,多态性(您在此处尝试实现的目标)效果更好 - 即,当基类中的方法是纯虚拟时,实现在任何派生类中。这与其他语言中的 interfaces 类似。

答案 1 :(得分:3)

这是完全正确的,你使用的obj指针属于ABC类型,所以根据多态性的原理,它只有ABC类可用的方法。当然,如果你考虑一下,这是完全正确和明智的。

如果你真的需要访问派生对象的特殊方法,你需要一个这种类型的指针,因为在你这样做之前它不会是那种类型。

答案 2 :(得分:3)

在我看来,你的设计很糟糕。你想做什么是不可能完成的。

想一想......调用返回DirextX9设备类型的函数的代码如何知道该函数是否存在?如果它获得指向DirectX10功能实例的ABC *会发生什么?

在我看来,你应该做的是弄清楚所有版本的DirectX中存在的主要抽象(作为一个Unix极客是一个我完全不熟悉的API,认为你应该使用交叉平台OpenGL代替)。然后,为每个抽象创建一个接口,并将其转换为ABC。每个ABC的接口仅提及其他ABC。然后,根据特定版本的DirectX API的类型和功能,为每个抽象派生一个特定的类。

现在,您最终可能会遇到这样的事情:

class Sprocket;

class Widget {
 public:
    virtual void squeak() = 0;
    virtual bool nudgeSprocket(Sprocket *sp);
};

class Sprocket {
 public:
    virtual void turn() = 0;
};

class GoldWidget {
 public:
    void squeak();
    bool nudgeSprocket(Sprocket *sp);
};

class GoldSprocket {
 public:
    void turn();
    virtual int glitter();
};

class SilverWidget {
 public:
    void squeak();
    bool nudgeSprocket(Sprocket *sp);
};

class SilverSprocket {
 public:
    void turn();
    virtual int shine();
};

现在,可能只有你用黄金或白银制造的机器,而不是两者。您可能认为GoldWidget::nudgeSprocket(Sprocket *sp)需要能够在其GoldWidget::glitter()参数上调用sp,并且这将非常安全。在我(也可能是其他人)看来,这是一个设计错误。

两件事之一应该是真的。 nudgeSprocket方法应该根据通用Sprocket接口实现,或者应该有一个知道它正在处理GoldWidget的类,并且会调用{{1}的版本以nudgeSprocket为参数。

我建议阅读有关design patterns的内容,因为我认为了解这种特殊的思考方式可以帮助您组织有关如何做到这一点的想法,并产生相对简洁的设计。特别是,我怀疑某些组合中使用的Adapter patternBridge patternFacade pattern可能会帮助您解决问题。

答案 3 :(得分:2)

DoSomethingElse是Derived的成员,而不是ABC,因此编译器不能假设您可以在ABC类型的指针上调用它。您可以向上转换为Derived *,或者将ABC *更改为Derived *,因为您知道它是派生的,或者您可以将DoSomethingElse移动到Abstract类。

答案 4 :(得分:0)

因为基类对象不能引用不属于它的东西。

答案 5 :(得分:0)

听起来你想使用RTTI(http://en.wikipedia.org/wiki/Run-time_type_information) - 如果指针碰巧是SubClassFoo,你可以使用其他功能。如果没有,您可能会对特殊功能感到不幸,但可能会优雅地降级。