我在使用继承和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>
)假设你的列表是有序的,所以这适用于如果你保持你的列表一直有序。
要把数据拉出来,需要考虑两件事:
- 如果你使用复制构造函数重新创建对象,你是在创建新对象,改变它们不会改变列表中的对象,所以最好使用指针
- 你必须根据存储对象的类型拆分代码路径
你可以这样做:
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
}