STL列表-作为指针对象的数据类型



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

假设我有一个具有两个派生类的抽象基类(其中定义了所有比较操作符)。列表声明为

list<StoreItem*> items;

我插入一个派生类(抽象基类StoreItem的),称为Food或Clothing。我创建了一个即将插入的新的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);

我做错了什么吗?另外,我如何从库存中提取信息?(例如:Food tempFruit(**iter)使用复制构造函数)。

提前感谢!祝你过得愉快。

您假设从列表中提取的项目是Food实例;然而,编译器并不知道这一点。当您从列表中的项目(表面类型为StoreItem的项目)构建Food的新实例时,您正在尝试调用Food::Food(const StoreItem)或兼容的东西。为什么?因为迭代器指向StoreItem*,所以可以是StoreItem对象的实例,或者是从StoreItem派生的任何类的实例,例如Food

正如其他评论者所评论的那样,多态性是成功的关键。你真的需要知道这个项目是Food吗?如果没有,则访问所有存储项(如价格、序列号等)共享的接口。如果你需要知道物品的具体信息,那么你可以试着推断它的类型:
Food *food = dynamic_cast<Food*>(*iter);
if (food != NULL) {
   // perform food related logic
   std::cout << "Ingredients: " << food->ingredients() << std::endl;
}
else {
   std::cout << "I can't eat that!" << std::endl;
}

如果您已经定义了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&类型的情况下对其执行许多操作——多态性是您的朋友。)

如果可以在两个指针指向基类之间定义比较操作符,则无需编写任何其他代码即可获得有序集合。根据您的应用程序,您可能需要一个集合或堆,甚至可能是一个映射。下面是做这件事的成语……(base从string公开派生).

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*;然后,它就像魔术一样插入std::set(或其他有序容器)的默认比较器参数中。

您可以求助于boost::ptr_list,而不是自行酿造任何解决方案。如果您打算像容器一样在STL中存储指针,那么它会使工作变得容易得多。然后,您所需要的就是为要插入的任何项定义operator<。请记住,ptr_list不打算用于共享所有权。要实现这一点,在std::list中使用std::shared_ptrS,并为shared_ptr类型专门使用std::less

通过为指向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. 你必须根据存储对象的类型拆分代码路径

你可以这样做:

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
}

相关内容

  • 没有找到相关文章

最新更新