【问题标题】:Abstract object cannot declare抽象对象不能声明
【发布时间】:2011-05-13 20:24:03
【问题描述】:

我遇到了抽象/虚拟类的问题,这里是问题的复制:

#include <iostream>

class A
{
protected:
    virtual std::string getDateTime() = 0;
    virtual void Write(std::string data, bool addDate) = 0;
    virtual bool CheckFile() = 0;
    virtual bool OpenFile(std::string path) = 0;
    virtual void CloseFile() = 0;
};

class B
    : public A
{
public:
    virtual std::string ToString() { return ""; };
    virtual void Write(std::string data) { };
};

class C
    : public A
{
protected:
    std::string getDateTime()
    {
        return "TODAY";
    };

    void Write(std::string data, bool addDate)
    {
        std::cout << "BasicClassA Write" << std::endl;
    };

    bool CheckFile()
    {
        std::cout << "BasicClassA CheckFile" << std::endl;
        return true;
    };

    bool OpenFile(std::string path)
    {
        std::cout << "BasicClassA OpenFile" << std::endl;
        return true;
    };

    void CloseFile()
    {
        std::cout << "BasicClassA CloseFile" << std::endl;
    };
};

class D
    : public B,
      public C
{
public:
    BasicClassB();
    virtual ~BasicClassB();

    std::string ToString()
    {
        return "BasicClassB tostring";
    };

    void Write(std::string data)
    {
        std::cout << "BasicClassB Write" << std::endl;
    };
};

int main(int ac, char *av[])
{
    BasicClassB b;
    std::cout << b.ToString() << std::endl;
    b.Write("");
    return 0;
}

这有一个编译错误:

../src/main.cpp: 在函数'int main(int, char**)':
../src/main.cpp:82:错误:不能将变量“b”声明为抽象类型“BasicClassB”
../src/main.cpp:64: 注意:因为以下虚函数在‘BasicClassB’中是纯函数:
../src/main.cpp:13:注意:虚拟 std::string BaseClassA::getDateTime()
../src/main.cpp:14: 注意: virtual void BaseClassA::Write(std::string, bool)
../src/main.cpp:15:注意:虚拟布尔 BaseClassA::CheckFile()
../src/main.cpp:16:注意:虚拟布尔 BaseClassA::OpenFile(std::string)
../src/main.cpp:17: 注意:virtual void BaseClassA::CloseFile()

也许我在这里漏掉了重点,但是 BaseClassA(即 BasicClassA)的实现应该包含这些函数,并且由于 BasicClassB 也是 BasicClassA 的子类,它也应该包含这些函数?

我错过了什么?我应该怎么做才能编译?

[编辑] I updated the class names as suggested by the comment
澄清一下:我在 A 类中使用纯虚拟来强制任何子级实现这些功能。

似乎虚拟继承是我需要的,但是,在我的情况下,我似乎没有得到正确的方法来做到这一点......

目标是有几个“基”类,有点像接口,强制子类实现函数,但它们的任何子类都应该继承被覆盖的函数(就像虚拟继承一样)

但是,使用任意组合 类任何:公共虚拟任何其他{} 不起作用并且总是给出相同的编译错误(上面的那个)。也许我需要改变的不仅仅是继承中的虚拟?

【问题讨论】:

  • 可能会更好地调用类 A B C D,因为当前的名称非常混乱。

标签: c++ compilation virtual abstract-class


【解决方案1】:

默认情况下它在 C++ 中不会这样工作 - 您需要菱形继承模式,但在 C++ 中您会得到不同的根:因此 BasicClassA 和 BaseClassB 都有自己的 BaseClassA(vtable 和实例变量)。

您可能想使用Virtual Inheritance

为了更清楚地了解非虚拟继承:

#include <iostream>


class A
{
    public:
        A(int x) {m_a = x;}
        virtual ~A() {}
        int m_a;
        virtual int getA() {return m_a;}
};

class B : public A
{
    public:
        B() : A(1) {}
};

class C : public A
{
    public:
        C() : A(2) {}
};

class D : public B,
          public C
{
};

void useB(B* b)
{
    std::cout << "useB:" << b->getA() << std::endl;
}

void useC(C* c)
{
    std::cout << "useC:" << c->getA() << std::endl;
}

int main()
{
    D* d = new D();
    useB(d);
    useC(d);

    return 0;
}

这会产生输出:

useB:1
useC:2

这个例子展示了虚拟继承,以及你想要的那种混合行为。

#include <iostream>


class A
{
    public:
        A(int x) {m_a = x;}
        virtual ~A() {}
        int m_a;
        virtual int getA() {return m_a;}
        virtual int virt() = 0;
};

class B : virtual public A
{
    public:
        B() : A(1) {}
};

class C : virtual public A
{
    public:
        C() : A(2) {}
        virtual int virt() {return 42;}
};

class D : public B,
          public C
{
    public:
        D() : A(3) {}
};



void useB(B* b)
{
    std::cout << "useB:" << b->getA() << std::endl;
}

void useC(C* c)
{
    std::cout << "useC:" << c->getA() << std::endl;
    std::cout << "useC-virt:" << c->virt() << std::endl;
}

int main()
{
    D* d = new D();
    useB(d);
    useC(d);

    return 0;
}

输出:

useB:3
useC:3
useC-virt:42

注意:来自 C 和 B 的构造函数在设置 m_a 时没有发言权,它是 D() 构造函数初始化列表的控制器。

编辑: 将虚拟应用到您的代码:

#include <iostream>

class A
{
protected:
    virtual std::string getDateTime() = 0;
    virtual void Write(std::string data, bool addDate) = 0;
    virtual bool CheckFile() = 0;
    virtual bool OpenFile(std::string path) = 0;
    virtual void CloseFile() = 0;
};

class B
    : virtual public A
{
public:
    virtual std::string ToString() { return ""; };
    virtual void Write(std::string data) { };
};

class C
    : virtual public A
{
protected:
    std::string getDateTime()
    {
        return "TODAY";
    };

    void Write(std::string data, bool addDate)
    {
        std::cout << "C Write" << std::endl;
    };

    bool CheckFile()
    {
        std::cout << "C CheckFile" << std::endl;
        return true;
    };

    bool OpenFile(std::string path)
    {
        std::cout << "C OpenFile" << std::endl;
        return true;
    };

    void CloseFile()
    {
        std::cout << "C CloseFile" << std::endl;
    };
};

class D
    : public B,
      public C
{
public:
    std::string ToString()
    {
        return "D tostring";
    };

    void Write(std::string data)
    {
        std::cout << "D Write" << std::endl;
    };
};

int main(int ac, char *av[])
{
    D b;
    std::cout << b.ToString() << std::endl;
    b.Write("");
    return 0;
}

【讨论】:

  • 非常感谢您的示例!我从你们那里得到一些文档后发现的答案略有不同(从class B 中删除public class A 并让class C 实现virtual public A,但这失去了class B 孩子实现class A 函数的要求,一种或另一种方式。
【解决方案2】:

BaseClassA 有 5 个纯虚函数。甚至具有一个纯虚函数的类是“抽象类”。纯虚函数(简而言之)的目的是禁止创建抽象类的对象。

为了实例化 BaseClassB,它需要定义您在 BaseClassA 中声明为纯虚拟的所有 5 个函数。 (在没有这些定义的情况下,BaseClassB 也变为 Abstract,因此您无法从中创建对象)。

【讨论】:

  • 我最喜欢理解纯虚函数哲学的例子是几何形状:class Shape { virtual float area() = 0; };类矩形:公共形状{浮动w;浮动 h;浮动区域() { 返回 w*h; }; };类搞笑形状:公共形状{}; [不允许实例化 FunnyShape 是有意义的,因为您无法计算其面积]。
【解决方案3】:

BasicClassB 仅派生自BaseClassA,这是一个抽象类,因为这些方法:

virtual std::string getDateTime() = 0;
virtual void Write(std::string data, bool addDate) = 0;
virtual bool CheckFile() = 0;
virtual bool OpenFile(std::string path) = 0;
virtual void CloseFile() = 0;

纯虚拟的

错误信息非常清楚:要能够实例化 BasicClassB,您必须提供上述方法的实现。

另外,请注意您在BasicClassB 中对Write 的定义:

virtual void Write(std::string data) { };

BaseClassA中的不同:

virtual void Write(std::string data, bool addDate) = 0;

所以这个方法还是需要实现BasicClassB才能实例化。

【讨论】:

    【解决方案4】:

    在函数中添加“=0”这一事实意味着它们是虚拟的,并且必须在子类中实现。这显然不是你想要的。如果您从具有基类实现的函数中删除“=0”,它应该可以按预期工作。

    【讨论】:

      猜你喜欢
      • 2021-06-10
      • 2011-02-21
      • 2012-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-12
      • 1970-01-01
      相关资源
      最近更新 更多