假设我在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
类可以返回list<something>
,但我还需要使用base
指针获取列表,例如:
base* base_ptr = new A();
base_ptr->get();
我必须做什么?
我是否要返回指向此列表的指针?参考?
我是否要从类B
的方法返回空指针?或者,当我尝试使用B
对象获取列表时,是否要抛出异常?或者我是否要更改base
类方法get
,使其不纯,并在base
类中执行此操作?
我还有其他事吗?
答案 0 :(得分:9)
你没有别的事可做。您提供的代码正是如此。
当你得到一个指向基类的指针时,由于该方法是在基类中声明的,并且是虚拟的,所以实际的实现将在类虚函数表中查找并适当调用。
所以
base* base_ptr = new A();
base_ptr->get();
将调用A :: get()。你不应该从实现中返回null(你不能,因为null无法转换为std :: list&lt; something&gt;无论如何)。您必须在A / B中提供实现,因为基类方法被声明为纯虚拟。
修改强>:
你不能只有A返回一个std :: list&lt;的东西&gt;而不是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是一个纯接口(它没有成员变量或非纯方法)。
<强> EDIT2 强>:
根据评论,您似乎希望能够在两个类之间具有公共接口,但能够执行一个实现所执行的操作,但另一个不提供。这是一个非常令人费解的场景,但我们可以从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();
}
这是令人费解的,但这种方法有几个优点:
您明确了解GetSomething()的语义:它允许您返回可用于检索某些内容列表的接口。
如何返回一个空的std :: list?
这是可能的,但糟糕的设计,就像拥有可以给可口可乐和百事可乐的自动售货机,除了它从未服务百事可乐;这是误导,最好避免。
返回一个boost :: optional&lt;的std ::列表&LT;的东西&gt; &GT; ? (正如Andrew所说)
我认为这是一个更好的解决方案,比返回和界面更好,有时可能是NULL,有时不是,因为那时你明确知道它是可选的,并且没有错误。
缺点是它会在你的界面中加强,我更愿意避免(这取决于我使用boost,但接口的客户端不应该被迫使用boost)。
答案 1 :(得分:1)
如果您需要能够不返回(在B类中)
,请返回boost::optional
class base
{
public:
virtual boost::optional<std::list<something> > get() = 0;
};
答案 2 :(得分:1)
你在做什么是错的。如果两个派生类都不常见,那么你可能不应该在基类中使用它。
除此之外,没有办法实现你想要的。您还必须在B中实现该方法 - 这正是纯虚函数的含义。但是,您可以添加特殊的失败案例 - 例如返回空列表,或者包含一个包含预定无效值的元素的列表。