从异构std :: list中检索数据

时间:2013-04-03 19:31:16

标签: c++ list

在下面的代码中,我只是试图尝试一个Heterogeneous std :: list,其中我在类型Base *的列表中存储了三个Derived类对象。从列表中检索数据时,我遇到了一些问题。我该怎么做呢?以下代码有效,因为所有三个类的定义都相同。所以编译器以某种方式设法给我预期的输出。

#include <list>
#include <iostream>
#include <string.h>

class Base;
typedef std::list<Base*> any_list;

class Base{};

class Derived1 : public Base
{
public:
    std::string s;
    Derived1():s("D1"){}
};
class Derived2 : public Base
{
public:
    std::string s;
    Derived2():s("D2"){}
};
class Derived3 : public Base
{
public:
    std::string s;
    Derived3():s("D3"){}
};

int main(int argc, char **argv) {
    any_list l;
    l.push_back(new Derived1);
    l.push_back(new Derived2);
    l.push_back(new Derived3);

    for(any_list::iterator itr=l.begin();itr!=l.end();++itr)
        std::cout<<((Derived1*)*itr)->s;
}

请注意o / p是 -

  

D1   D2   D3

如果我在任何类中添加一个额外的成员(这是预期和正确的),这不起作用。那么我应该如何对数据进行类型转换并从异构列表中检索它呢? 我在这里错过了什么吗?

4 个答案:

答案 0 :(得分:1)

最简单的方法可能是定义Base,如下所示:

class Base {
public:
    virtual const std::string& getData() = 0;
}

然后让各种派生类适当地实现getData()。这样,您的输出循环可能只是:

for (Base* b : l) {
    cout << b->getData();
}

答案 1 :(得分:1)

我会重新定义Base以获得输出信息的虚拟方法:

class Base {
    friend std::ostream & operator << (std::ostream &os, const Base &b) {
        b.print(os);
        return os;
    }
    virtual void print (std::ostream &os) const = 0;
public:
    virtual ~Base () {}
};

然后,您必须在每个派生类中实现print方法。您的打印循环看起来像:

    for (any_list::iterator itr=l.begin();itr!= l.end();++itr)
        std::cout << **itr;

您的列表应避免管理裸指针,因为您将自己置于泄漏内存的风险之中。您可以改用智能指针:

typedef std::list< std::unique_ptr<Base> > any_list;

由于unique_ptr需要从指针显式构造,因此需要更新填充列表的代码。

    l.push_back(std::unique_ptr<Base>(new Derived1));
    l.push_back(std::unique_ptr<Base>(new Derived2));
    l.push_back(std::unique_ptr<Base>(new Derived3));

答案 2 :(得分:0)

您的演员(Derived1*)*itr相当于static_cast,其中声明:

  

如果“指向cv1 B的指针”类型的prvalue指向实际上是B类型对象的子对象的D,则生成的指针指向封闭对象类型为D。否则,演员表的结果是未定义的。

因此,您有未定义的行为,因为您正在将指向Derived2Derived3个对象的指针转换为Derived1*

实现此目的的正确方法是在std::string s;中定义Base并为其提供一个带有std::string参数的构造函数。然后,您可以使Derived1Derived3的构造函数使用适当的字符串参数调用此构造函数。

如果您向其中一个Derived类添加任何额外成员,您将无法通过多态Base*访问它们。然而,尝试这样做是设计糟糕的表现。您应该只将Base*指向的对象视为Base个对象。也就是说,Base描述了这些对象的接口。

如果确实需要执行特定于指向Base*的对象的派生类的操作,则可以使用dynamic_cast

if (Derived1* p = dynamic_cast<Derived1*>(*itr)) {
  // This condition will only be true if the object pointed to by `*itr` really is a Derived1
}

答案 3 :(得分:0)

#include <list>
#include <vector>
#include <iostream>
#include <string.h>

class Base;
typedef std::list<Base*> any_list;

class Base{
public:
    virtual ~Base(){}
    virtual std::string getData()=0;
};

class Derived1 : public Base
{
public:
    std::string s;
    Derived1():s("D1"){}
    std::string getData()
    {
        return s;
    }
};
class Derived2 : public Base
{
public:
    std::string s;
    Derived2():s("D2"){}
    std::string getData()
    {
        return s;
    }
};
class Derived3 : public Base
{
public:
    std::string s;
    Derived3():s("D3"){}
    std::string getData()
    {
        return s;
    }
};

int main(int argc, char **argv) {
    any_list l;
    l.push_back(new Derived1);
    l.push_back(new Derived2);
    l.push_back(new Derived3);

    for(any_list::iterator itr=l.begin();itr!=l.end();++itr)
        std::cout<<((Base*)*itr)->getData()<<std::endl;
}

感谢您的帮助。混乱很明显,问题就解决了。什么是 -

  

typedef std :: list&lt; std :: unique_ptr&gt; any_list;

我在unique_ptr命名空间中找不到任何名为std::的内容。 :(