已实现的继承 class

inheritance of an implemented class

这可能是一个简单的问题,请耐心等待,因为我已经习惯了Java...

假设我们有一个接口:

class IDoable {
    virtual void do() = 0;
};

另一个class:

class Base : public IDoable {
    //...
    virtual void do() { ... }
};

最后 class 扩展我们的基地 class:

class ExtendingBase : public Base {
    // some extra functionality
};

如果我想列出 IDoable 个对象,可以是 Base 个对象或 ExtendingBase 个对象,我在这部分迷路了。我是否必须在 Base class 中添加一些方法声明?这方面如何运作?

编辑:

我有一些类型为 IDoable 的指针列表 如果我随后尝试向该列表添加一个 Base 对象,我会收到错误消息:

IDoable is an ambiguous base of Base

如果我尝试添加一个 ExtendingBase 对象,同样如此

IDoable is an ambiguous base of ExtendingBase

您必须在 Base 中实现 do() 函数,因为 class IDoable 中的函数是纯虚拟函数。

如果您决定创建一个 ExtendingBase 对象,do() 函数的行为将与它在 Base 中实现的一样,除非您通过在 [=13] 中重新实现它来覆盖它=].

IDoable *pDo1 = new Base();
IDoable *pDo2 = new ExtendingBase();
pDo1->do();
pDo2->do();
delete pDo1;
delete pDo2;

if I want to make a list of IDoable objects

您不能创建 IDoable 对象期限。它是一个抽象class,它不能直接构造,所以你不能拥有它们的容器。您可以做的以及您可能打算做的是拥有一个 IDoable*:

的容器
std::vector<IDoable*> objects;
objects.push_back(new Base);
objects.push_back(new ExtendedBase);

或者在 C++11 中更好地表达所有权:

std::vector<std::unique_ptr<IDoable>> objects;

给定您的界面,您已经可以对这些对象中的任何一个调用 do(),这将通过虚拟分派做正确的事情。不过,您肯定希望将一个成员函数添加到您的界面中:

class IDoable {
public:
    virtual ~IDoable() = default; // this one
    virtual void do() = 0;
};

这样,当您 delete 一个 IDoable* 时,您将 delete 完整的对象,而不仅仅是基本接口。

您的第一个也是最主要的问题是您在 Java 中的思考。 "interface" 和 "extending" 这两个词非常 Java 导向。 C++ 不这么认为。

例如,当有人在 C++ 上下文中谈论 "interface" 时,我可能认为他在谈论 .h 文件中的 class decleration(与位于.cpp 文件)

IDoable 是 CLASS。时期。唯一的区别是它有一个禁止实例化的纯虚函数。除此之外,它的行为类似于 class,它可以继承自,可以包含成员变量和其他任何东西。

您只需要确保抽象函数在某些派生 class 中被覆盖,以便 class 生成对象。

如是说:

//in the stack:
Base base;
ExtendingBase eBase;
base.do();
eBase.do()

//in the heap with IDoable as pointer:
IDoable * base = new Base();
IDoable * ebase = new ExtendingBase ();
base->do();
ebase->do();

现在,您可能会问 - 如何激活 Base 和 ExtendingBase 功能?所以就像 Java 一样,你需要转换指针然后才调用正确的函数。

 Base* realBase = (Base*)base;
realbase->someBaseFunction();

由于 C++ 中的许多东西,这段代码有点危险。您可以改用 dynamic_cast

最后一件事 - do 是 C++ 中的关键字,它不能声明函数名称。

由于do 是一个纯虚方法,它必须在派生的class 中实现。您不能拥有 IDoable 对象的向量或数组,因为您无法实例化此类对象。不过,您可以有一个向量或指针数组或对对象的引用。

如果您创建一个 ExtendingBase 对象并调用 do 函数,它将调用 Base class' 一个(因为 ExtendingBase 继承了方法)。

当您从基 class 指针或引用调用 do() 函数时,虚拟多态性开始发挥作用:do() 函数适用于指向或引用的对象的动态类型将被称为:

class IDoable{
public:
  virtual void dof()=0;
  virtual ~IDoable() = default;
};


class Base:public IDoable{
public:
virtual void dof(){std::cout << "Base";}
virtual ~Base() = default;
};

class ExtendingBase:public Base{
 public:
  virtual void dof() { std::cout << "ExtendingBase"; }
};

int main()
{
    IDoable *ptr = new Base(); // A smart pointer would be a better choice
                               // but for clarity's sake I'm using bare
                               // memory allocations here
    ptr->dof(); // Walks the virtual table and calls "Base"
    delete ptr;

    ptr = new ExtendingBase();
    ptr->dof(); // Walks the virtual table and calls "ExtendingBase"
    delete ptr;
}

还要注意虚析构函数的使用:它们像普通虚函数一样工作,因此当在基指针上调用 delete 时,为了实际破坏正确类型的对象(即在层次结构中调用正确的析构函数) ,您需要将其设为虚拟。

作为旁注:do 是 C++ 中的保留关键字


响应您的编辑:如果您有一个向量或一个 IDoable 指针列表,您不能只向其中添加一个派生对象,但您应该添加一个 指针 到派生对象。 IE。以下是错误的:

std::vector<IDoable*> vec;
vec.push_back(Base());

加一个基础 class 仍然是 class(在 C++ 中没有 接口 概念,如 Java),你应该' t 多次从基数 class 继承:

class Base:public IDoable{
...
class ExtendingBase:public Base, public IDoable <- nope
...

这只会导致识别基本子对象时出现问题。 我建议阅读 C++ 中的 dreaded diamond problem(这是解决基数 class 在继承层次结构中多次出现的一种方法。无论如何,一个好的设计可能首先会避免这种情况)。