Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/dart/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用虚拟方法纠正行为_C++_Inheritance_Polymorphism_Virtual Functions - Fatal编程技术网

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)。由于基类方法声明为纯虚拟,因此必须在A/B中提供实现

编辑

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

如果您确实不希望B拥有get方法,请不要从base继承。 有些备选方案是:

在B::get()中引发异常: 您可以在B::get()中抛出一个异常,但请确保您解释了您的基本原理,因为这是违反直觉的。我知道这是一个非常糟糕的设计,你可能会让人们对你的基类感到困惑。这是一个漏洞百出的抽象,最好避免

独立界面: 为此,您可以将base分解为单独的接口:

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::optional>怎么样?(安德鲁建议)

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


缺点是它会将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
实际上是-a
Base
并且在
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; 
};