2013-04-03 43 views
1

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

#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

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

回答

1

最简单的方法可能会定义基地,像这样:

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

,然后让不同的派生类实现getData()适当。这样一来,你的输出循环可能仅仅是:

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

你投(Derived1*)*itr相当于static_cast其中规定:

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

所以,你有不确定的行为,因为你是铸造指向在Derived2Derived3对象到Derived1*指针。

正确的方式实现这一目标是有Base定义std::string s;,并给它一个构造函数一个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 
} 
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)); 
+0

上面列出的解决方案的“fancier”版本可以在这里找到(http://ideone.com/YXFq64)。 – jxh 2013-04-05 06:02:27

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;

我在std::命名空间中没有找到任何名为unique_ptr的东西。 :(

+0

要获得unique_ptr,您需要包含'',并编译您的代码与C++ 11编译器(使用GCC,您使用'-std = C++ 11'命令行选项)。 – jxh 2013-04-04 19:34:47