STL List - 数据类型作为指针对象

时间:2011-10-19 03:35:04

标签: c++ inheritance pointers stl abstract-base-class

我在使用继承和STL列表库时遇到问题......

说,我有一个带有两个派生类的抽象基类(其中定义了所有比较运算符)。该列表声明为

list<StoreItem*> items;

我正在插入一个名为Food或Clothing的派生类(抽象基类StoreItem)。我创建了一个即将插入的新StoreItem指针:

StoreItem* item = new Food(arguments here);

现在,我想将这个新项目(按顺序)插入到列表中,我的尝试是这样的:

list<StoreItem*>::iterator iter;
for (iter = inventory.begin(); iter != inventory.end(); iter++)
{
    if (*item < **iter)
        break; // break out to insert
}

inventory.insert(iter, item);

我有什么问题吗?另外,我如何从库存中提取信息? (例如:使用复制构造函数的食物tempFruit(** iter))。

提前谢谢!祝你有个美好的一天。

5 个答案:

答案 0 :(得分:1)

如果您定义了StoreItem::operator<,这将有效,但还有另一种方法可能会更好一点。 STL已经冷落了。您可以为<定义StoreItem*,然后使用list<...>::sort()

(你可能已经想过定义你自己的SortedItemList类来处理内部的排序。)

是的,tempMovie(**iter)除了其他方式外还可以使用。

修改:

我想我过早谈到从库存中抽出一些东西。这有效:

list<StoreItem *>::iterator citr = items.begin();

Food *fp = dynamic_cast<Food *>(*citr);

Food ff(*fp);

请注意,您必须知道此StoreItem*实际指向Food - 如果它指向Clothing,您将会出现细分错误或更糟。要找到答案,您可以实现自己的StoreItem::whatTypeAmI(),或使用C ++的运行时类型识别:

#include <typeinfo>
...
Food a;
StoreItem *sp = *citr;
if(typeid(*sp)==typeid(a))
{
  // it's a Food
}

(请注意,您可以使用StoreItem*StoreItem&做很多事情而不知道它的类型 - 多态性是您的朋友。)

答案 1 :(得分:1)

您假设您从列表中提取的项目是Food个实例;但是,编译器不知道这一点。当您从列表中的项目(具有明显类型Food的项目)构造StoreItem的新实例时,您尝试调用Food::Food(const StoreItem)或兼容的内容。为什么?因为迭代器指向StoreItem*可以StoreItem对象的实例,或者是从StoreItem派生的任何类的实例,例如{{ 1}}。

正如其他海报所评论的那样,多态性是 成功的关键。你真的需要知道这个项目是Food吗?如果没有,则访问所有商店项目共享的界面(如价格,序列号等)。如果您需要了解有关该项目的具体信息,那么您可以尝试推断其类型:

Food

答案 2 :(得分:0)

如果可以将两个指针之间的比较运算符定义到基类,则无需编写任何其他代码即可获得有序集合。根据您的应用程序,您可能需要一个集合或堆,甚至可能需要一个映射。这是成功的习惯......(基础是从字符串公开派生的。)

template<>
struct std::less<base*>
{
   bool operator()(const base* lhs, const base* rhs) const
   {
      return *lhs < *rhs;
   }
};

typedef set<base*> S;

int _tmain(int argc, _TCHAR* argv[])
{
    base able(std::string("able"));
    base baker(std::string("baker"));
    base charlie(std::string("charlie"));

    S inventory;
    inventory.insert(&charlie);
    inventory.insert(&able);
    inventory.insert(&baker);

    for (S::iterator i = inventory.begin(); i != inventory.end(); ++i)
        std::cout << **i << endl;
    return 0;
}

输出:
能够
贝克
查理

在发现这个成语之前,可以碾磨一段时间。发生了什么事情,你是专门为图书馆模板std :: less for T = base *;然后,就像使用magic一样插入std :: set(或其他有序容器)的默认比较器参数。

答案 3 :(得分:0)

您可以诉诸boost::ptr_list而不是自制任何解决方案。如果您打算在STL中存储像容器这样的指针,它会让生活变得更容易。然后,您只需要为要插入的任何项目定义operator<。请注意,ptr_list并非旨在与共享所有权一起使用。要实现此目的,请在std::shared_ptrS中使用std::list,并为std::less类型使用shared_ptr

答案 4 :(得分:0)

使用为StoreItem指针定义的比较函数,您可以缩短插入代码,如下所示:

bool less_ptr( const StoreItem*& lhs, const StoreItem*& rhs )
{
    return *lhs < *rhs;
}

插入:

StoreItem* item = new Food(arguments here);
inventory.insert( std::upper_bound( inventory.begin(), inventory.end(), item, less_ptr ), item);

std::upper_bound#include <algorithm>)假设您的列表已排序,因此如果您始终对列表进行排序,这将适用。

关于撤回数据,有两点需要考虑:

  1. 如果使用复制构造函数重新创建对象,则创建新对象并更改它们不会更改列表中的对象,因此最好使用指针
  2. 您必须根据存储对象的类型拆分代码路径
  3. 你可以这样做:

    Food* foodObj = NULL;
    Clothing* clothesObj = NULL;
    
    list<StoreItem *>::iterator it = inventory.find( /* something */ );
    StoreItem* item = *it;
    
    item->DoSomethingWithAnyStoreItem(); // It's best to only use such methods
    
    // But if you need something only a derived class has...
    foodObj = dynamic_cast<Food*>(item);
    clothesObj = dynamic_cast<Clothes*>(item);
    
    if( foodObj != NULL )
    {
        foodObj->DoSomethingWithFood();
        Food newFood( *foodObj );
        newFood.DoSomethingWithCopyOfFood();
    }
    else if( clothesObj != NULL )
    {
        clothesObj->DoSomethingWithClothes();
    }
    else
    {
        // It's neither Food, nor Clothes
    }