std::map 中的继承,基类作为值

Inheritance in std::map with base class as value

本文关键字:基类 继承 map std      更新时间:2023-10-16

这是一个代码:

class Base {
    public:
        long index;
};
class Derived : public Base {
    public:
        bool value;
};
void call(map<char *, Base *> *base) {
    map<char *, Base *>::iterator it = base->begin();
    cout << it->second->index << endl;
}
void test(void) {
    map<char *, Derived *> *d = new map<char *, Derived *>;
    call(d);
}

编译器会发出错误警报:

error C2664: 'call' : cannot convert parameter 1 from 'std::map<_Kty,_Ty> *' to 'std::map<_Kty,_Ty> *'
1>        with
1>        [
1>            _Kty=char *,
1>            _Ty=Derived *
1>        ]
1>        and
1>        [
1>            _Kty=char *,
1>            _Ty=Base *
1>        ]
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

我明白为什么会发生此错误。我不明白如何让它工作。什么样的演员阵容以及如何使用它?

UPD

很抱歉不精确,让我解释更多细节。我有两组数据,由 A 类和 B 类表示。这两个类都有一个公共成员 - 例如"索引"。两组都包装成一张地图(特别感谢 Rob 对 char* 进行了重大更正):

std::map<char, ClassA>
std::map<char, ClassB>

有时我需要遍历两个映射以获得公共成员"索引"的值。我试图避免代码重复,只制作一个函数来迭代两个映射。

我想我可以提取一个带有公共成员的超类,并使用如下所示的参数创建一个函数:

std::map<char, SuperClassAB>

std::map<char, SuperClassAB>::iterator

但看起来这是一个坏主意。

UPD2

一个聪明人给了我解决方案:

template <class T>
void call(map<char, T> *base) {
    map<char, T>::iterator it = base->begin();
    cout << it->second->index << endl;
}
void test(void) {
    map<char, Derived *> d;
    call(&d);
}

似乎还没有人建议这样做,但您也可以call函数模板

    template <class Type>
    void call(const std::map<char*,Type*> & base)
    {
        static_assert(std::is_base_of<Base, Type>::value,
                      "Function is only callable with maps "
                      "whose mapped_type is derived from Base");
        /* More stuff */
    }

这样,该函数可以使用BaseDerived和从Base派生的任何其他函数来调用。

不能强制转换这些类型。 map<char*,Base*>map<char*,Derived*>就像stringfloat一样不同。

最简单的方法是test()简单地填充一个map<char*,Base*>并用它调用call。 在Base(可能是纯virtual)中提供virtual方法,并在Derived中实现这些功能。

您可以尝试从map<char*,Base*> transform map<char*,Derived*>,但为了做到这一点,您的函子需要:

  1. 绝对知道Base*实际上指向一个Derived对象,并使用static_cast
  2. 使Base多态(通常通过实现virtual析构函数),并使用dynamic_cast

如果您的目标是以某种方式在派生类上"操作",请在基类上创建一个虚拟方法并重写它以获得所需的特定行为:

class Base
{
public:
   long index;
   virtual void doSomething()
   {
      // Do something with index
      cout << index << endl;
   }
};
class Derived : public Base
{
public:
   bool value;
   virtual void doSomething()
   {
      // Do something with value
      cout << value << endl;
   }
};
// NOTE:  I removed the pointer and made it a ref.
// NOTE:  I made it "const" because you probably don't want to alter the
// map.  If you do...
void call(const map<char *, Base *>& base)
{
   map<char *, Base *>::const_iterator it = base.begin();
   //   cout << it->second->index << endl;
   it->second->doSomething();
}
void test(void)
{
   map<char *, Base *> d;
   // Push some members into d...I didn't, but you should
   // if you want call(...) to be meaningful.
   call(d);
}

这有帮助吗?

您可能需要容器来存储基类指针,而不是派生类指针。

例如map<char *, Base *> *d;.

此后,您应该使用所需的任何派生类型的元素填充地图。 例如

char keyA = 'A';
char keyB = 'B';
(*d)[&keyA] = new Derived();
(*d)[&keyB] = new AnotherDerived();

这在您的实例中有效吗?

顺便说一句,你为什么使用char *作为密钥似乎是一个奇怪的选择。