如果你从一开始就无法理解问题标题,那不是你的错 - 我想不出更好的描述。以下是对问题的解释,可能有点冗长,所以请提前道歉。
在我的程序的初始版本中,我有一个Ecosystem类和一个Individual类:
// Very simplified, for illustration purposes
class Ecosystem
{
protected:
// The int is just the ID of the individual.
std::map<int, std::shared_ptr<Individual> > individuals;
public:
Ecosystem();
void func(int _individual_id)
{
std::cout << "Individual's age: "
<< individuals[_individual_id]->get_age()
<< std::endl;
}
void routine(int _individual_id)
{
// Another function working via
// the pointers in individuals.
}
// More such functions...
};
class Individual
{
protected:
int age;
public:
Individual();
inline int get_age() const
{
return age;
}
};
Ecosystem类包含许多功能,我将来会添加更多功能。
我现在决定将Individual类拆分为基类和两个派生类,比如TypeAIndividual和TypeBIndividual,因为它们每个都有成员和属性,而另一个不需要(他们也通过共享一些成员和属性)基类)。所以我有基类Individual类和两个派生类:
class TypeAIndividual : public Individual
{
protected:
// Data structures specific to individuals of type A
public:
TypeAIndividual();
};
class TypeBIndividual : public Individual
{
protected:
// Data structures specific to individuals of type B
public:
TypeBIndividual();
};
问题是现在生态系统也需要分为TypeAEcosystem和TypeBEcosystem:
class Ecosystem
{
protected:
// Holding pointers to the base Individual class is pointless (pun not intended)
// std::map<int, std::shared_ptr<Individual> > individuals;
public:
Ecosystem();
// I want to keep func() in the base class
// because it only accesses attributes and
// members common to both classes derived
// from Individual.
void func(int _individual_id)
{
// Hmmmm...
// The pointers don't live in the Ecosystem class any more!
std::cout << "Individual's age: "
<< individuals[_individual_id]->get_age()
<< std::endl;
}
// OK to implement in each class
// derived from Ecosystem.
virtual void routine(int _individual_id) = 0;
};
class TypeAEcosystem : public Ecosystem
{
protected:
// Pointers to individuals
// of the corresponding type.
std::map<int, std::shared_ptr<TypeAIndividual> > individuals;
public:
TypeAEcosystem();
// Reimplementing routine() is OK
// because it does things specific to
// this individual type.
virtual void routine (int _individual_id)
{
// Operate on data structures particular
// to this type of individual.
}
};
class TypeBEcosystem : public Ecosystem
{
protected:
// Pointers to individuals
// of the corresponding type.
std::map<int, std::shared_ptr<TypeBIndividual> > individuals;
public:
TypeBEcosystem();
// Reimplementing routine() is OK
// because it does things specific to
// this individual type.
virtual void routine (int _individual_id)
{
// Operate on data structures particular
// to this type of individual.
}
};
TypeAEcosystem和TypeBEcosystem都使用void func(int _individual_id)
,它需要访问相应类型的个人。但基类Ecosystem不再包含指向个体的指针,因为std::map
在每个派生类中而不在基类中。
我的问题是:我如何访问相应类型的个人(TypeAIndividual
或TypeBIndividual
),同时避免在源自生态系统的每个类中实现单独的void func(int _individual_id)
?换句话说,有没有办法在基类中保留func()
,这样当我更改它时,我不必对派生类进行更改?在实际的程序中,有许多函数,例如func()
,只需要int
作为参数。此外,其中一些函数从Ecosystem
类中的其他结构中获取单个ID,因此我不能简单地将指针传递给TypeAIndividual
或TypeBIndividual
。
将TypeAIndividual
和TypeBIndividual
合并回一个公共Individual
类,其中包含两个派生类所需的所有数据结构。这让我觉得这是一种特别笨拙的做事方式,但至少它会起作用。
制作func()
&amp;公司虚拟并在TypeAEcosystem
和TypeBEcosystem
中实施它们。这意味着如果我想对任何功能进行更改,我必须更改两种实现(=维护噩梦)。
只有一个Ecosystem
类,其中包含两种类型的std::map
个人,如下所示:
// Seems clunky...
class Ecosystem
{
protected:
// Note: The Ecosystem can contain
// one OR the other, but not both!
// One map will always be empty.
std::map<int, std::shared_ptr<TypeAIndividual> > type_a_individuals;
std::map<int, std::shared_ptr<TypeBIndividual> > type_b_individuals;
public:
Ecosystem();
void func(int _individual_id)
{
// Check what type of individuals we
// are working with and operate on the
// appropriate container.
if (type_a_individuals.size() > 0)
{
std::cout << "Individual's age: "
<< type_a_individuals[_individual_id]->get_age()
<< std::endl;
}
else
{
std::cout << "Individual's age: "
<< type_b_individuals[_individual_id]->get_age()
<< std::endl;
}
}
};
这需要在每个函数中插入一个检查,这在可维护性方面几乎与在单独的类中使用函数一样糟糕。
注意:虽然我非常希望避免传递指针,但如果它解决了问题,我会考虑适当地进行向上转换和/或向下转换(作为最后的手段......)。 / p>
欢迎任何建议!
<小时/> 修改1
谢谢大家的精彩回复!正如amit和Chris所建议的那样,看着我的Ecosystem
课程,果然,它太笨重了。我将成员函数移动到其他类中,现在我在Ecosystem
类中有四到五个基本函数。 Ecosystem
类驻留在库中,并提供了与个人进行实验的界面,但我不希望用户能够直接操作Individual
和其他类,所以我做不到完全远离它。
我喜欢所有建议,有一些巧妙的解决方案。话虽这么说,克里斯提出的那个立刻引起了我的注意,因为它非常整洁,允许我有一个单独的Ecosystem类而不是三个独立的类(base和two派生)。个人的类型可以在配置文件中指定,我可以在同一个实验中从不同的配置文件中生成多个生态系统。这是公认的答案。
再次感谢大家的建设性意见!
答案 0 :(得分:0)
查看实现细节,我假设这些。生态系统是个人的容器/处理器类。查看界面,id似乎在不同的个体中是唯一的,即,invidual和B个体不能具有相同的id。 如果这些都是真的,我将坚持一个生态系统类,它定义了访问个体的接口,可以存储在地图中(基本指针*),因为ids是唯一的。然后,如果您想知道正在请求哪种类型,可以使用外部的动态强制转换,并且可以使用各个类的多态接口完成其他操作。我还强烈建议您使用Herb Sutter http://www.gotw.ca/publications/mill18.htm#Notes建议的实施方案,因为许多专家高度承认它的可扩展性。
答案 1 :(得分:0)
您可以在EcoSystem
中添加虚拟方法以检索通用TypeIndividual
:
class EcoSystem
{
public:
void func(int _individual_id) {
std::cout << "Individual's age: "
<< get_individual(_individual_id).get_age()
<< std::endl;
}
virtual const TypeIndividual& get_individual(int _individual_id) const = 0;
virtual void routine(int _individual_id) = 0;
};
对于每个子类:
class TypeAEcosystem : public Ecosystem
{
protected:
// Pointers to individuals of the corresponding type.
std::map<int, std::shared_ptr<TypeAIndividual> > individuals;
public:
const TypeIndividual& get_individual(int _individual_id) const override
{
return *individuals.at(_id);
}
// Reimplementing routine() is OK
// because it does things specific to
// this individual type.
void routine (int _individual_id) override
{
// Operate on data structures particular
// to this type of individual.
}
};
答案 2 :(得分:0)
两个派生Ecosystem
&}在存储和访问Individual
的方式上有所不同。这使得Individual
访问行为成为虚拟的好例子。有一些返回类型的协方差,它看起来很好:
struct Individual { void baseStuff() {} };
struct TypeAIndividual : Individual { void typeAStuff() {} };
struct Ecosystem {
void func(int id) {
individual(id).baseStuff();
}
virtual void routine(int id) = 0;
protected:
virtual Individual &individual(int id) = 0;
};
struct TypeAEcosystem : Ecosystem {
TypeAIndividual &individual(int id) override {
return *_individuals.at(id);
}
void routine(int id) override {
individual(id).typeAStuff();
}
private:
std::map<int, std::shared_ptr<TypeAIndividual>> _individuals;
};
由于地图及其访问者除了个人类型之外是相同的,因此您可以将它们分解为中间模板基类:
template <class Individual>
struct DerivedEcosystem : Ecosystem {
Individual &individual(int id) override {
return *_individuals.at(id);
}
private:
std::map<int, std::shared_ptr<Individual>> _individuals;
};
struct TypeAEcosystem : DerivedEcosystem<TypeAIndividual> {
void routine(int id) override {
individual(id).typeAStuff();
}
};
答案 3 :(得分:0)
正如我在评论中已经说过的那样,您可以考虑将Ecosystem作为模板化类,并为每个IndivualType设置一个生态系统实例。
template <class IndivualType>
class Ecosystem {
protected:
// The int is just the ID of the individual.
std::map<int, std::shared_ptr<IndivualType> > individuals;
public:
// ...
};
如果您需要Ecosystem在给定的IndividualType中表现不同,您还可以明确地专门化您的生态系统,如下所示:
template <>
class Ecosystem<SpecialIndividualType> {
protected:
// The int is just the ID of the individual.
std::map<int, std::shared_ptr<SpecialIndividualType> > individuals;
public:
// special implementation for EcoSystem for SpecialIndividualType
};
这可能没有必要,但可能很高兴知道。
最后,如你所说
The Ecosystem class contains dozens of functions, and I will add a lot more in the future.
您可能需要考虑将生态系统的功能划分为策略。我不知道你的需求,但仅作为一个例子:
template <class IndivualType, class SomePolicy1, class SomePolicy2>
class Ecosystem {
private:
const SomePolicy1 mSp1;
const SomePolicy2 mSp2;
protected:
// The int is just the ID of the individual.
std::map<int, std::shared_ptr<IndivualType> > individuals;
public:
Ecosystem (const SomePolicy1& sp1= SomePolicy1(), const SomePolicy2& sp2= SomePolicy2())) : mSp1(sp1), mSp2(sp2) {}
// ...
void func(int _individual_id)
mSp1.doSmth(_individual_id);
}
void func2(int _individual_id) {
mSp2.doSmth(_individual_id);
}
};
这称为&#34;基于策略的设计&#34;,您可以在网上找到关于它的大量信息。
当然还有其他解决方案,例如如前所述将方法设为虚拟。我可能会尝试两种方式(取决于你的时间),看看你最满意的是什么。