3

假设我在接口中有一个纯虚方法,base它返回给我一个列表something

class base 
{ 
public:
     virtual std::list<something> get() = 0; 
}; 

假设我有两个继承该类的base类:

class A : public base 
{ 
public:
     std::list<something> get();
}; 

class B : public base 
{ 
public:
     std::list<something> get(); 
};

我希望只有A类可以返回 a list<something>,但我还需要有可能使用base指针获取列表,例如:

base* base_ptr = new A();
base_ptr->get();

我必须做什么?

我必须返回指向此列表的指针吗?参考?

我必须从类的方法返回一个空指针B吗?还是在尝试使用B对象获取列表时抛出异常?还是我要更改base类方法get,使其不纯并在base课堂上完成这项工作?

我需要做点别的吗?

4

3 回答 3

9

你没有别的事可做。您提供的代码正是这样做的。

当你得到一个指向基类的指针时,由于方法是在基类中声明的,并且是虚拟的,实际的实现将在类的虚函数表中查找并适当地调用。

所以

base* base_ptr = new A();
base_ptr->get();

将调用 A::get()。你不应该从实现中返回 null (你不能,因为 null 无论如何都不能转换为 std::list< something >)。由于基类方法被声明为纯虚拟,因此您必须在 A/B 中提供实现。

编辑

你不能只让 A 返回一个 std::list< something > 而不是 B 因为 B 也继承了基类,并且基类有一个必须在派生类中重写的纯虚方法。从基类继承是一种“is-a”关系。我能看到的唯一另一种方法是从类中私有继承,但这会阻止派生到基的转换。

如果您真的不希望 B 具有 get 方法,请不要从 base 继承。一些替代方案是:

在 B::get() 中抛出异常: 您可以在 B::get() 中抛出异常,但请确保充分解释您的基本原理,因为它违反直觉。恕我直言,这是一个非常糟糕的设计,您可能会混淆使用您的基类的人。这是一个有漏洞的抽象,最好避免。

单独的界面: 您可以为此将基础分解为单独的界面:

class IGetSomething
{
public:
    virtual ~IGetSomething() {}
    virtual std::list<something> Get() = 0;
};

class base
{
public:
    // ...
};

class A : public base, public IGetSomething
{
public:
    virtual std::list<something> Get()
    {
        // Implementation
        return std::list<something>();
    }
};

class B : public base
{

};

这种情况下的多重继承是可以的,因为 IGetSomething 是一个纯接口(它没有成员变量或非纯方法)。

编辑2

根据评论,您似乎希望能够在两个类之间拥有一个公共接口,同时能够执行一个实现执行但另一个实现不提供的某些操作。这是一个相当复杂的场景,但我们可以从 COM 中获得灵感(先别开枪):

class base
{
public:
    virtual ~base() {}
    // ... common interface

    // TODO: give me a better name
    virtual IGetSomething *GetSomething() = 0;
};

class A : public Base
{
public:
    virtual IGetSomething *GetSomething()
    {
        return NULL;
    }
};

class B : public Base, public IGetSomething
{
public:
    virtual IGetSomething *GetSomething()
    {
        // Derived-to-base conversion OK
        return this;
    }
};

现在你可以做的是:

base* base_ptr = new A();
IGetSomething *getSmthing = base_ptr->GetSomething();
if (getSmthing != NULL)
{
    std::list<something> listOfSmthing = getSmthing->Get();
}

它很复杂,但这种方法有几个优点:

  • 您返回公共接口,而不是具体的实现类。

  • 您将继承用于其设计目的。

  • 很难误用:base没有提供std::list get(),因为它不是具体实现之间的常用操作。

  • 您明确了 GetSomething() 的语义:它允许您返回一个接口,该接口可用于检索某物的列表。

    只返回一个空的 std::list 怎么样?

这是可能的,但糟糕的设计,就像拥有一台可以提供可口可乐和百事可乐的自动售货机,但它从不供应百事可乐;它具有误导性,最好避免。

只返回一个 boost::optional< std::list< something >> 怎么样?(由安德鲁建议)

我认为这是一个更好的解决方案,比返回和有时可能为 NULL 有时不是的接口更好,因为这样您就明确知道它是可选的,并且不会有任何错误。

缺点是它将 boost 放在你的界面中,我更愿意避免这种情况(使用 boost 取决于我,但界面的客户端不应该被迫使用 boost)。

于 2012-07-06T08:13:19.947 回答
1

你在做什么是错误的。如果它对两个派生类都不通用,那么您可能不应该在基类中拥有它。

除此之外,没有办法实现你想要的。您还必须在 B 中实现该方法-这正是纯虚函数的含义。但是,您可以添加一个特殊的失败案例——例如返回一个空列表,或者一个包含一个包含预定无效值的元素的列表。

于 2012-07-06T08:21:49.427 回答
1

boost::optional如果您需要不返回的能力,请返回(在 B 类中)

class base 
{ 
public:
     virtual boost::optional<std::list<something> > get() = 0; 
}; 
于 2012-07-06T08:13:27.757 回答