C++ 使用虚拟方法纠正行为
假设我在C++ 使用虚拟方法纠正行为,c++,inheritance,polymorphism,virtual-functions,C++,Inheritance,Polymorphism,Virtual Functions,假设我在base接口中有一个纯虚拟方法,它向我返回某物的列表: class base { public: virtual std::list<something> get() = 0; }; 我希望只有A类可以返回列表,但我还需要能够使用基指针获取列表,例如: base* base_ptr = new A(); base_ptr->get(); 我该怎么办 我必须返回指向此列表的指针吗?推荐人 我必须从类B的方法返回空指针吗?或者,当我尝试使用B对象获取列
base
接口中有一个纯虚拟方法,它向我返回某物的列表
:
class base
{
public:
virtual std::list<something> get() = 0;
};
我希望只有A
类可以返回列表
,但我还需要能够使用基
指针获取列表,例如:
base* base_ptr = new A();
base_ptr->get();
我该怎么办
我必须返回指向此列表的指针吗?推荐人
我必须从类B
的方法返回空指针吗?或者,当我尝试使用B
对象获取列表时,是否必须抛出异常?或者我必须更改base
类方法get
,使其不纯粹,并在base
类中执行此工作
我还要做点别的吗?你没有别的事要做。您提供的代码正是这样做的 当您得到一个指向基类的指针时,由于该方法是在基类中声明的,并且是虚拟的,因此实际的实现将在类虚拟函数表中查找并相应地调用 所以 将调用一个::get()。您不应该从实现中返回null(当然不能,因为null不能转换为std::list
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
{
};
现在你能做的是:
base* base_ptr = new A();
IGetSomething *getSmthing = base_ptr->GetSomething();
if (getSmthing != NULL)
{
std::list<something> listOfSmthing = getSmthing->Get();
}
base*base_ptr=newa();
IGetSomething*getSmthing=base_ptr->GetSomething();
if(getsMathing!=NULL)
{
std::list listOfSmthing=getSmthing->Get();
}
它是复杂的,但这种方法有几个优点:
- 返回的是公共接口,而不是具体的实现类
- 您使用继承来实现它的设计目的
- 很难错误地使用:base不提供std::list get(),因为它不是具体实现之间的常见操作
- GetSomething()的语义是明确的:它允许您返回一个接口,该接口可用于检索某事物的列表 只返回一个空的std::list怎么样
缺点是它会将boost放在你的界面中,这是我更愿意避免的(由我决定使用boost,但界面的客户端不必被迫使用boost)。返回
boost::可选以防你需要不返回的能力(在B类中)
类基
{
公众:
虚拟boost::可选get()=0;
};
您所做的是错误的。如果这两个派生类都不通用,则可能不应该在基类中使用它
除此之外,没有办法实现你想要的。您还必须在B中实现该方法-这正是纯虚函数的含义。但是,您可以添加一个特殊的失败案例,例如返回一个空列表,或者一个包含一个预先确定的无效值的元素的列表。如果它只特定于a类
,它不应该只在a类
中,而不在基本类
中吗?为什么?如果您有一个指向基的指针
,则始终可以使用static\u cast
或dynamic\u cast
将其强制转换为a*
,具体取决于您是否知道具体类型。(编辑:将A
更改为A*
)此处的设计有问题。如果您必须依赖于基类指针指向的具体类型的隐式知识,以便可以对其进行强制转换,那么您最好不要传递基类指针,因为您已经失去了多态性的好处。在这种情况下,B
在概念上显然不是base
,因此不应该从base
派生出来。Meyers第32项:确保公共继承模型“是-a”
。对于基类为真的一切对于派生类都应该为真。如果B
实际上是-aBase
并且在B
上调用get()
是无效的,那么在Base
上调用get()
应该是无效的。@Nick如果我正确阅读了其他问题,那么拥有基类的唯一原因就是能够将a
和B
存储在同一个列表中。我想你现在做的是在一个列表上迭代(意思是你得到一个base*
),然后尝试调用get()
?当您的列表包含B
对象时,您希望发生什么?好的,但如果我编写base*base_ptr=new B()代码>行为是什么
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
{
};
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();
}
class base
{
public:
virtual boost::optional<std::list<something> > get() = 0;
};