1

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

#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

4 回答 4

1

Easiest way might be to define Base like so:

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

And then have your various derived classes implement getData() as appropriate. That way, your output loop could just be:

for (Base* b : l) {
    cout << b->getData();
}
于 2013-04-03T19:38:35.543 回答
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));
于 2013-04-03T19:58:41.130 回答
0

Your cast (Derived1*)*itr is equivalent to a static_cast which states:

If the prvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

So you have undefined behaviour because you are casting pointers that point at Derived2 and Derived3 objects to a Derived1*.

The proper way to achieve this is to have std::string s; defined in Base and give it a constructor that takes a std::string argument. Then you can make the constructors of Derived1 to Derived3 call this constructor with the appropriate string argument.

If you add any extra members to one of the Derived classes, you won't be able to access them through the polymorphic Base*. However, trying to do that is a sign of bad design. You should only be treating the objects that a Base* points at as though they were Base objects. That is, Base describes the interface for those objects.

If you really need to do something that is derived class specific to an object pointed to a Base*, you can use a 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
}
于 2013-04-03T19:38:38.357 回答
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< std::unique_ptr > any_list;

我没有找到命名空间中调用unique_ptr的任何内容std::。:(

于 2013-04-04T19:24:39.143 回答