引用来自C ++中另一个(不相关的)类的基类的派生类的对象 [英] Referring to an object of a derived class from the base class of another (unrelated!) class in C++
问题描述
如果你从一开始就不能理解问题标题,这不是你的错 - 我想不出更好的描述。
在我的程序的初始版本中,我有一个生态系统类和一个个人类:
//非常简化,为了说明目的
class Ecosystem
{
protected:
// int只是个人的ID。
std :: map< int,std :: shared_ptr< Individual> >个人;
public:
Ecosystem();
void func(int _individual_id)
{
std :: cout< 个人年龄:
<个人[_individual_id] - > get_age()
< std :: endl;
}
void routine(int _individual_id)
{
//通过
工作的另一个函数//个体中的指针。
}
//更多这样的函数...
};
class个人
{
protected:
int age;
public:
Individual();
inline int get_age()const
{
return age;
}
};
Ecosystem类包含了许多函数,未来我会添加更多。 p>
我现在决定将Individual类拆分为一个基类和两个派生类,比如TypeAIndividual和TypeBIndividual,因为它们都有成员和属性,而另一个不需要(他们也通过基类共享几个成员和属性)。所以我有基类Individual类和两个派生类:
class TypeAIndividual:public Individual
{
protected:
//特定于A类个人的数据结构
public:
TypeAIndividual();
};
class TypeBIndividual:public Individual
{
protected:
//特定于B类个人的数据结构
public:
TypeBIndividual();
};问题是,生态系统现在还需要拆分为TypeAEcosystem和TypeBEcosystem: class Ecosystem
{
protected:
//保存指向基础的指针个别类是无意义的不想要)
// std :: map< int,std :: shared_ptr< Individual> >个人;
public:
Ecosystem();
//我想在基类
//中保留func(),因为它只访问属性和
//从Individual派生的两个类共同的成员
//。
void func(int _individual_id)
{
// Hmmmm ...
//指针不再存在于生态系统类中了!
std :: cout<< 个人年龄:
<个人[_individual_id] - > get_age()
< std :: endl;
}
//可以在每个类中实现
//从Ecosystem派生。
virtual void routine(int _individual_id)= 0;
};
类TypeAEocosystem:public生态系统
{
protected:
//指向个人
//对应类型的指针。
std :: map< int,std :: shared_ptr< TypeAIndividual> >个人;
public:
TypeAEcosystem();
//重新实现routine()是OK
//因为它的事情特定于
//这个单独的类型。
virtual void routine(int _individual_id)
{
//在数据结构上操作特定的
//到这种类型的个体。
}
};
类TypeBEcosystem:public生态系统
{
protected:
//指向个人
//对应类型的指针。
std :: map
public:
TypeBEcosystem();
//重新实现routine()是OK
//因为它的事情特定于
//这个单独的类型。
virtual void routine(int _individual_id)
{
//在数据结构上操作特定的
//到这种类型的个体。
}
};
TypeAEcosystem和TypeBEcosystem都使用 void func(int _individual_id)
,它需要访问相应类型的个人。但是基类Ecosystem不包含指向个人的指针,因为 std :: map
在每个派生类中,而不在基类中。
我的问题是:如何访问适当类型的个人( TypeAIndividual
或 TypeBIndividual
),同时避免在从Ecosystem衍生的每个类中实现单独的 void func(int _individual_id)
换句话说,有没有办法保持 func()
在基类中,以便当我更改它,我不必更改派生类?在实际的程序中,有几个函数 func()
只需要一个 int
作为参数。另外,这些函数中的一些函数从 Ecosystem
类中的其他结构中获取单独的ID,因此我不能简单地传递一个指向 TypeAIndividual
或 TypeBIndividual
。
我认为的事情
-
合并 TypeAIndividual
和 TypeBIndividual
回到一个通用的 Individual
两个派生类。
-
制作 func()
& Co.虚拟并在 TypeAEcosystem
和 TypeBEcosystem
中实现。这意味着如果我想更改任何功能,我必须更改这两个实现(=维护噩梦)。
-
生态系统
类包含两种类型个体的 std :: map
,如下:
//似乎笨重...
class生态系统
{
protected:
//注:生态系统可以包含
//一个或另一个,但不能同时包含两个!
//一个地图总是空的。
std :: map< int,std :: shared_ptr< TypeAIndividual> > type_a_individuals;
std :: map< int,std :: shared_ptr< TypeBIndividual> > type_b_individuals;
public:
生态系统();
void func(int _individual_id)
{
//检查我们
//正在使用的个人类型,并在
//适当的容器上操作。
if(type_a_individuals.size()> 0)
{
std :: cout< 个人年龄:
< type_a_individuals [_individual_id] - > get_age()
<< std :: endl;
}
else
{
std :: cout< 个人年龄:
< type_b_individuals [_individual_id] - > get_age()
<< std :: endl;
}
}
};
这需要在每个函数中插入一个检查,这在可维护性方面几乎和在不同类中的函数一样糟糕。
注意:虽然我非常想避免传递指针,我会考虑向上转换和/或向下转换(如果它解决了问题)。
欢迎任何建议! / p>
编辑1
梦幻般的回应!正如amit和Chris所建议的,并且看着我的生态系统
类,当然,它太庞大了。我把成员函数移动到其他类,现在我下来在生态系统
类中的四个或五个基本功能。 生态系统
类驻留在库中,并提供了一个用于与个人进行实验的接口,但我不希望用户能够操作 Individual
s和其他类直接,所以我不能完全取消它。
我喜欢所有的建议,有一些巧妙的解决方案。话虽如此,克里斯提出的建议立即抓住了我的注意,因为它非常整洁,允许我有一个单一的生态系统类,而不是三个单独的类(基础和两个派生)。个人的类型可以在配置文件中指定,并且我可以在同一个实验中从不同的配置文件生成多个生态系统。这是被接受的答案。
再次感谢大家的建设性输入!
解决方案 正如我在评论中所说的,你可以考虑将生态系统作为一个模板化类,并为每个IndivualType生成一个生态系统实例。
code> template< class IndivualType>
class Ecosystem {
protected:
// int只是个人的ID。
std :: map< int,std :: shared_ptr< IndivualType> >个人;
public:
// ...
};
如果您需要生态系统对给定IndividualType的行为不同,生态系统如此:
模板<>
class Ecosystem< SpecialIndividualType> {
protected:
// int只是个人的ID。
std :: map< int,std :: shared_ptr< SpecialIndividualType> >个人;
public:
// EcoSystem for SpecialIndividualType的特殊实现
};
这可能不是必须的,但是这可能是好事。
最后,正如你所说的
生态系统类包含了许多函数,我将来会添加更多。
您可能想考虑将您的生态系统的功能分为政策。我不知道你的需要,但只是作为一个例子:
template< class IndivualType,class SomePolicy1,class SomePolicy2>
class Ecosystem {
private:
const SomePolicy1 mSp1;
const SomePolicy2 mSp2;
protected:
// int只是个人的ID。
std :: map< int,std :: shared_ptr< IndivualType> >个人;
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);
}
};
这称为基于策略的设计,您可以在网上找到很多关于它的信息。
当然还有其他解决方案,例如使方法虚拟,如前所述。我可能会尝试两个(取决于你有的时间),看看你最舒服的感觉。
If you can't understand the question title from the onset, it's not your fault - I couldn't think of a better description. Here is the explanation of the problem, which might be a bit lengthy, so apologies in advance.
In the initial version of my program, I had an Ecosystem class and an Individual class:
// 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;
}
};
The Ecosystem class contains dozens of functions, and I will add a lot more in the future.
I have now decided to split the Individual class into a base class and two derived classes, say TypeAIndividual and TypeBIndividual, because they each have members and attributes that the other one does not need (they also share a few members and attributes via the base class). So I have the base Individual class and two derived classes:
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();
};
The problem is that the ecosystem now also needs to be split into TypeAEcosystem and 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 and TypeBEcosystem both use void func(int _individual_id)
, which needs to access individuals of the corresponding type. But the base class Ecosystem doesn't contain pointers to individuals any more because the std::map
s are in each derived class and not in the base class.
My question is: how can I access the appropriate type of individual (TypeAIndividual
or TypeBIndividual
) while avoiding implementing separate void func(int _individual_id)
in each class derived from Ecosystem? In other words, is there a way to keep func()
in the base class so that when I change it, I don't have to make changes to the derived classes? In the actual program, there are dozens of functions like func()
which take just an int
as a parameter. Also, some of those functions take individual IDs from other structures in the Ecosystem
class, so I can't simply pass a pointer to TypeAIndividual
or TypeBIndividual
.
Things I have considered
Merging TypeAIndividual
and TypeBIndividual
back into a common Individual
class with all the data structures necessary for both derived classes. This strikes me as a particularly clumsy way of doing things, but at least it will work.
Making func()
& Co. virtual and implementing them in TypeAEcosystem
and TypeBEcosystem
. This means that if I want to make a change in any of the functions, I have to change both implementations (= a maintenance nightmare).
Having only one Ecosystem
class which holds std::map
s of the two types of individuals, like this:
// 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;
}
}
};
This would require inserting a check in every function, which is almost as bad in terms of maintainability as having the functions in separate classes.
Note: Although I would very much like to avoid passing pointers around, I would consider upcasting and/or downcasting as appropriate (as a last resort...) if it solves the problem.
Any suggestions are welcome!
Edit 1
Thank you all for the fantastic responses! As suggested by both amit and Chris, and looked at my Ecosystem
class and sure enough, it was too bulky. I moved member functions around into other classes and now I'm down to four or five essential functions in the Ecosystem
class. The Ecosystem
class resides in a library and provides an interface for conducting experiments with individuals, but I don't want users to be able to manipulate Individual
s and other classes directly, so I can't do away with it completely.
I liked all suggestions, there are some ingenious solutions. That being said, the one proposed by Chris grabbed my attention immediately for being very neat and allowing me to have a single Ecosystem class rather than three separate classes (base and two derived). The type of individual can be specified in a config file, and I can spawn multiple ecosystems from different config files within the same experiment. This is the accepted answer.
Thank you again everyone for the constructive input!
解决方案 As I already said in my comment you could consider making Ecosystem a templated class and have one instance of an Ecosystem for each 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:
// ...
};
In case you need the Ecosystem to behave different for a given IndividualType, you can in addition explicitly specialize your Ecosystem like so:
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
};
This probably will not be necessary, however it may be good to know.
Finally as you said the
The Ecosystem class contains dozens of functions, and I will add a lot more in the future.
You may want to consider to split the functionality of your ecosystem into policies. I dont know your needs but just as an example:
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);
}
};
This is called "policy based design", you can find a lot of information about it on the web.
Of course there are other solutions as well, such as making the methods virtual as already mentioned. I would probably try both (depending on the time you have) and see what you feel most comfortable with.
这篇关于引用来自C ++中另一个(不相关的)类的基类的派生类的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文