我正在努力将我对儿子的兴趣从魔兽争霸3编程扩展到C ++,以扩大他的视野到一定程度。我们计划移植他写的一个小游戏。
上下文就是这样的。
我拼凑了一个小例子来完成这项工作,所以我们可以讨论主题(列表,模板等),但我对结果不满意。
#include <iostream>
#include <list>
using namespace std;
/* Base class to hold static list in common with various object groups */
template<class T>
class ObjectManager
{
public :
ObjectManager(void)
{
cout << "Construct ObjectManager at " << this << endl;
objectList.push_back(this);
}
virtual ~ObjectManager(void)
{
cout << "Destroy ObjectManager at " << this << endl;
}
void for_each(void (*function)(T *))
{
for (objectListIter = objectList.begin();
objectListIter != objectList.end();
++objectListIter)
{
(*function)((T *) *objectListIter);
}
}
list<ObjectManager<T> *>::iterator objectListIter;
static list<ObjectManager<T> *> objectList;
};
/* initializer for static list */
template<class T>
list<ObjectManager<T> *> ObjectManager<T>::objectList;
/* A simple ship for testing */
class Ship : public ObjectManager<Ship>
{
public :
Ship(void) : ObjectManager<Ship>()
{
cout << "Construct Ship at " << this << endl;
}
~Ship(void)
{
cout << "Destroy Ship at " << this << endl;
}
friend ostream &operator<<(ostream &out,const Ship &that)
{
out << "I am a ship";
return out;
}
};
/* A simple missile for testing */
class Missile : public ObjectManager<Missile>
{
public :
Missile(void) : ObjectManager<Missile>()
{
cout << "Construct Missile at " << this << endl;
}
~Missile(void)
{
cout << "Destroy Missile at " << this << endl;
}
friend ostream &operator<<(ostream &out,const Missile &that)
{
out << "I am a missile";
return out;
}
};
/* A function suitable for the for_each function */
template <class T>
void show(T *it)
{
cout << "Show: " << *it << " at " << it << endl;
}
int main(void)
{
/* Create dummy planets for testing */
Missile p1;
Missile p2;
/* Demonstrate Iterator */
p1.for_each(show);
/* Create dummy ships for testing */
Ship s1;
Ship s2;
Ship s3;
/* Demonstrate Iterator */
s1.for_each(show);
return 0;
}
具体来说,通过继承机制,列表有效地嵌入到每艘船中。必须有一艘船才能进入船舶清单。必须有导弹才能进入导弹清单。这感觉很尴尬。
我的问题归结为“有更好的方法吗?”
我正在寻找更好的想法。 所有有用的条目都会得到一个upvote。
由于
恶
答案 0 :(得分:1)
尝试将容器设置为实体未派生的单独类。然后,如果您希望在创建时自动插入,请让实体构造函数通过引用获取容器并插入自身,或让容器也充当工厂,在其中创建实体并维护对它们的引用。像这样:
template<typename Object>
class ObjectManager {
public:
template<class Action> void forEach(Action action); // like std::for_each
Object& create(); // inserts into internal list
// ...
}
我会在这里注意到,这一切似乎都有点过度设计。 “ObjectManager”......名称本身听起来太通用了。但也许你正试图以“干净”的方式教授一些OOP的东西,为什么不呢。
答案 1 :(得分:1)
通过简单访问使用对象容器,即ObjectManager<T>::objectList
。
当您将ObjectManager<T>::for_each
更改为静态函数时,main
可能如下所示:
int main(void)
{
/* Create dummies */
Missile p1;
Missile p2;
Ship s1;
Ship s2;
Ship s3;
/* Demonstrate Iterators */
Missile::for_each(show);
Ship::for_each(show);
/* Demonstrate Direct Container Access */
Missile::objectList.pop_back();
Missile::for_each(show);
return 0;
}
顺便说一下,这是个人偏好的问题,但是我使用std::vector
代替std::list
来存储指向已创建对象的指针,因此要具有随机可访问性。
还有一件事:当它被销毁时从列表中删除它。
答案 2 :(得分:1)
我认为这里的问题是你正在扩展继承的概念。你拥有的是有效的代码和工作,但正如你所指出的那样,在你的子类的构造函数的幕后隐藏容器类是不对的。这是(在我看来)因为导弹与它的父类(容器)没有任何共同之处,当这是一个(正确地)被抽象出来的设计模式时,你正在使用继承来提供这个容器。标准模板库。正如John Zwinck所暗示的那样,工厂模式将提供一种更具表现力的方式来实现您想要的目标。
/* Container class to create and store various object groups */
template<class T>
class ObjectManager
{
public :
ObjectManager() : nextAvailableIndex(0)
{}
virtual ~ObjectManager(void)
{
for (objectListIter = objectList.begin(); objectListIter != objectList.end(); ++objectListIter)
delete *objectListIter;
}
T* ObjectManager::Create()
{
T* object = new Object(nextAvailableIndex++);
cout << "Constructed object " << object << endl;
objectList.push_back(object);
}
void for_each(void (*function)(T *))
{
for (objectListIter = objectList.begin(); objectListIter != objectList.end(); ++objectListIter)
{
(*function)((T *) *objectListIter);
}
}
int nextAvailableIndex;
list<T*>::iterator objectListIter;
list<T*> objectList;
};
int main(void)
{
/* Create dummy planets for testing */
ObjectManager<Missile> missileManager;
Missile* p1 = missileManager.Create();
/* Demonstrate Iterator */
missileManager.for_each(show);
/* Create dummy ships for testing */
ObjectManager<Ship> shipManager;
shipManager.Create();
shipManager.Create();
shipManager.Create();
/* Demonstrate Iterator */
shipManager.for_each(show); // will show three ships
return 0;
}
我遗漏了Missile和Ship的类定义,因为我放置的唯一更改是构造函数的一个整数作为唯一标识符。
答案 3 :(得分:1)
基本上,这是一个使用遗传的典型案例。 Missile和Ship不应该从ObjectManager继承。当你在物体之间有“一种”关系时(如“核导弹是一种导弹”),你应该保留继承。
这里你使用继承它的“机械”属性(自动注册新实例)但是为了实现相同的目的,它很容易制作对象,Missile说,派生自一些ManagedObject公共类并使它添加新的管理实例列表的一些唯一全局对象的实例(或者通过覆盖默认的new和删除导弹而不进行继承)。或者您可以将管理器作为instanciation参数传递给Missile类,或者通过管理器的一些构造函数方法创建Missiles,它们都将它添加到容器中。
所有这些解决方案都有效,并且比您的原始代码(也可以使用)更令人满意,并且可能还有其他我没有想到的。然而,上述提议中没有一个对我来说是完全令人满意的。根本问题是C ++缺乏适当的元类机制(C ++是关于编译时效率,而不是灵活性)。
在想出不同的可能性之后,我想出了下面的代码。该示例仅适用于Ship,不需要使用Missile或通过模板使用两个类的公共代码(即使它很容易)。我的答案集中在保持系统在给定时间内存在的所有Ship实例的最新列表,而不干涉继承。我之所以选择这个解决方案,是因为我认为它更多的是KISS(保持愚蠢和简单),而且当您需要维护代码时,复杂的解决方案是一个真正的负担。这个想法也是为了避免重新编码STL中已经存在的东西,比如for_each。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<class Ship*> all_ships;
class Ship
{
public:
Ship(void){
cout << "Construct Ship at " << this << endl;
all_ships.push_back(this);
}
~Ship(void){
cout << "Destroy Ship at " << this << std::endl;
// we remove it from the list
int i = 0;
while (all_ships[i] != this) { i++; }
all_ships[i] = all_ships.back();
all_ships.pop_back();
}
static void show(Ship *s){
cout << "ship :" << s << "\n";
}
};
void list_all_ships(){
cout << "List of Ships\n";
for_each(all_ships.begin(), all_ships.end(), Ship::show);
cout << "\n";
}
int main(void)
{
cout << "Two automatic (on stack) Ships\n";
Ship s1;
Ship s2;
list_all_ships();
cout << "One new dynamic (on heap) Ship\n";
Ship * s3 = new Ship();
list_all_ships();
cout << "Delete dynamic Ship\n";
delete s3;
list_all_ships();
cout << "The two dynamic Ships will be deleted when getting out of Scope\n";
return 0;
}
答案 4 :(得分:0)
为什么不让ObjectManager<T>::for_each()
成为静态方法?