【问题标题】:Unexpected conversion of inherited classes继承类的意外转换
【发布时间】:2014-01-28 12:31:35
【问题描述】:

我正在尝试制作一种“收藏”。这个类需要接受所有继承自基类的类。

例如:

假设我们有一个名为“A”的基类,并且我们有很多其他类继承自这个类“A”。所以我们有,类“B”继承自“A”,类“C”继承自“A”,等等,我们有一个名为“collection”的类,它也继承自“A”。类“collection”有一个“std::map”,使用“add”方法保存所有类,并且可以使用“get”检索。

发生的事情是它以某种方式工作,但应用程序转换为“基类”而不是返回传递的类。

我们上代码(看代码上的cmets理解输出):

这是工作代码:

#include <iostream>
#include <string>
#include <map>

class A
{
public:
    virtual void saySomething()
    {
        std::cout << "Hello from A object" << std::endl;
    }
};

class B : public A
{
public:
    void saySomething()
    {
        std::cout << "Hello from B object" << std::endl;
    }
};

class C : public A
{
    void saySomething()
    {
        std::cout << "Hello from C object" << std::endl;
    }
};

class Collection : public A
{
public:
    void add(const std::string & key, A * value)
    {
        storage[key] = value;
    }
    A * get(const std::string & key)
    {
        return storage[key];
    }
private:
    std::map<std::string, A *> storage;
};

int main( int argc, char ** argv )
{

    B * b = new B;
    C * c = new C;
    Collection * col = new Collection;
    Collection * col2 = new Collection;
    col->add("b", b); // works, because "b" inherits from "A" class.
    col->add("c", c); // works, because "c" inherits from "A" class.
    col->add("col2", col2); // watch this part...
    col->get("b")->saySomething(); // works - Output: Hello from B object
    col->get("c")->saySomething(); // works - Output: Hello from C object
    return 0;

}

它有效,但是当我尝试检索“col2”并使用他的方法时......

col->get("col2")->add("b", b);
// output: error: 'class A' has no member named 'add'

如果我没记错的话,它需要工作,因为“get”方法返回对象,所以我有“col2”对象的方法对吗?如果你回答是,我们有同样的想法,我不知道发生了什么。在“b”和“c”对象上,它返回对象并且我有“saySomething”方法,但是当我尝试检索“col2”时,它将该对象作为“A”对象,所以错误:

“A 类”没有名为“add”的成员

如果我这样做会变得更糟:

col->get("col2")->saySomething(); // output: Hello from A object

这个想法是,一个集合只接受从基类继承的类,也接受从同一基类继承的其他集合,比如递归。

【问题讨论】:

  • 你很惊讶吗?您的get 调用返回A*A*add() 方法吗?此外,您的Collection继承 来自A,但不覆盖 方法saySomething(),那么为什么您对输出感到惊讶?
  • 不要使用公共继承,除非继承的类“是”父类。 Collection 不是 A,因此从 A 公开继承它是糟糕的设计。
  • 如果要访问不属于As 接口的方法,则需要强制转换为Collection
  • @Nim - 你说的没有意义,因为我知道 A 没有实现方法“get”,但重要的是从 A 继承的“集合”具有该方法.
  • @tillaert - 请给我解释一下好吗?

标签: c++ inheritance


【解决方案1】:

C++ 有静态类型,所以当你有一个A 类型的对象时——这是你的get 方法返回的——只有在A 中声明的方法可以使用。要在get 的结果上调用add,您需要使用static_castdynamic_cast 进行强制转换,或者重新考虑您的类层次结构。为什么Collection 需要继承自A? (回答你的最后一个问题,因为它继承,如果你调用saySomething,它将调用A 中声明的方法,因为Collection 不提供自己的覆盖)。

【讨论】:

  • “这个想法是,一个集合只接受从基类继承的类,也接受从同一基类继承的其他集合,比如递归。” - 这就是集合也继承自 A 的原因。 - 你有一个关于我如何在那种情况下实现那些“static_cast”或“dynamic_Cast”的例子吗? - 我会看到更多关于这个的,谢谢。
  • 我为您看到以下选项: 1. 演员表,例如dynamic_cast(col->get("col2"))->add("b", b); (您应该测试强制转换是否返回非空) 2. 将集合方法添加到基类 3. 使用访问者设计模式。我认为 1 是最糟糕的 - 演员表是危险的并发症,并且通常是设计错误的信号。您可以考虑将收集方法添加到 A 并在 A 实现中抛出异常,而在 Collection 中覆盖执行应该做的事情。
  • 我喜欢你的回答。我在互联网上看到一些帖子对演员表也有同样的看法,说它很危险等等。关于在 A 上添加 Collection 方法我试过了,我得到了错误。
  • 我认为您只添加了没有大括号中的函数体的声明。
  • 我按照你说的做了,首先我只做了没有body的声明,看看会发生什么,因为Collection有方法,所以可能会覆盖,但是没有用,所以我用大括号中的主体声明,它起作用了。但我认为是:这很奇怪。我是说“A”类是“一切”,想象一下如果我有更多的类,我需要在 A 上声明所有类吗?我希望“A”类是一种类型,就是这样。有一种方法不需要在 A 类上声明所有方法吗?谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-23
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多