【问题标题】:STL List - Datatype as a pointer objectSTL 列表 - 作为指针对象的数据类型
【发布时间】:2011-10-19 03:35:04
【问题描述】:

我在使用继承和 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);

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

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

【问题讨论】:

  • 问题到底是什么?您是在问以您这样做的方式进行比较是否从根本上正确/错误,还是您的代码有问题?
  • 您是否为您的班级定义了运算符&lt;
  • 我主要遇到的问题是我不知道为什么这样做:Food tempMovie(*iter);它要求Food::Food(const StoreItem)的定义,当插入的**iter的数据类型显然首先是Food时。

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


【解决方案1】:

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

正如其他发帖者所评论的,多态性是a成功的关键。您真的需要知道该项目是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;
}

【讨论】:

  • 这与我的回答非常相似,内森。我的答案是在四处修补并考虑哪一方应该具有更高的优先级并首先执行之后得到的,我做了 ((Food*)(*iter))->food_method();。谢谢大佬!
  • 我很高兴它对你有用。小心 C 风格的铸造。您可以将迭代器的内容强制转换为Food 指针,但这并不意味着它实际上 指向Food 对象。这可能会导致意外的段错误、不可预测的行为、内存损坏等。
【解决方案2】:

只要您定义了StoreItem::operator&lt;,这将起作用,但还有另一种方法可能会更好一些。 STL 已经冷了。您可以为StoreItem* 定义&lt;,然后使用list&lt;...&gt;::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&amp; 做很多事情——多态是您的朋友。)

【讨论】:

  • 这个帮助最大;但是,不幸的是,我实际上将列表中的信息作为派生类之一提取时遇到了问题...
【解决方案3】:

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

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;
}

输出:

面包师
查理

在发现这个成语之前,可能会转悠一段时间。发生的事情是您正在专门针对 T=base*; 的库模板 std::less然后 this 就像魔法一样插入 std::set (或其他有序容器)的默认比较器参数。

【讨论】:

  • 这帮助我理解了一个不同的概念;但是,并没有按照我想要的方式帮助我。不过还是谢谢! :-)
【解决方案4】:

您可以求助于boost::ptr_list,而不是自制任何解决方案。如果您打算将指针存储在像容器一样的 STL 中,这会使生活变得容易得多。然后,您只需为您要插入的任何项目定义operator&lt;。请记住,ptr_list 不打算与共享所有权一起使用。要实现此目的,请在 std::list 中使用 std::shared_ptrS 并将 std::less 专门用于您的 shared_ptr 类型。

【讨论】:

    【解决方案5】:

    使用为指向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 &lt;algorithm&gt;) 假定您的列表已排序,因此如果您始终保持列表排序,这适用。

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

    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
    }
    

    【讨论】:

      猜你喜欢
      • 2020-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-09
      • 1970-01-01
      • 2014-01-26
      • 2014-03-18
      相关资源
      最近更新 更多