鸟类、蜜蜂和摘要 类
Birds, bees and abstract classes
新手我目前被这个看似简单的问题困住了。假设我想写一些关于一群动物从此幸福地繁殖的代码。显然,他们都需要一个 mate() 方法,所以我可以像这样定义一个抽象 class:
class FemaleAnimal {
public:
virtual FemaleAnimal mate(const MaleAnimal& a) const = 0;
virtual MaleAnimal mate(const MaleAnimal& a) const = 0;
}
并导出所有不同的物种:
class FemaleBird : public FemaleAnimal {
public:
FemaleBird mate (const MaleBird& b) const;
MaleBird mate (const MaleBird& b) const;
}
class FemaleBee: public FemaleAnimal {
public:
FemaleBee mate (const MaleBee& b) const;
MaleBee mate (const MaleBee& b) const;
}
在 main() 中,我想要两个向量
males = vector<MaleAnimal>
females = vector<FemaleAnimal>
每一个都可能包含鸟类和蜜蜂,并在 运行 时填充。每个索引的物种匹配,所以
如果males[i]是一只蜜蜂,那么females[i]也是一只蜜蜂,所以我可以做
vector<FemaleAnimal> femaleOffspring;
vector<MaleAnimal> maleOffspring;
for (int i=0; i<males.size(); ++i){
femaleOffspring.push_back( females[i].mate(males[i]) );
maleOffspring.push_back( females[i].mate(males[i]) );
}
现在,显然我想要派生的 classes 中的 mate() 方法来实现基础 class 中的方法,但是我必须为动物定义 mate()一般来说,比如
FemaleBee::mate(const MaleAnimal& a) const;
但是蜜蜂不与鸟类交配。我将如何实现这种专业化?这有一些特殊的设计模式吗?我试图研究协方差之类的东西,但这比帮助更令人困惑。
附加问题:当雄性[i] 和雌性[i] 在 运行 时间属于不同物种时,我如何捕获这种情况?
编辑:假设男性和女性来自完全不同的 class 等级制度,并且不能从一个共同的基础 class.
中派生出来
编辑:为了完整起见,这里是基于 n.m 的答案的最终解决方案。非常感谢大家!
#include <iostream>
using namespace std;
class GenericMale {
public:
virtual void test() = 0; // just to make the class abstract
};
template <typename Species>
class Male : public GenericMale
{
void test() {};
};
class GenericFemale {
virtual void tryMate (const GenericMale&) const = 0;
};
template <typename Species>
class Female : public GenericFemale
{
public:
virtual void tryMate (const GenericMale& m) const
{
try {
auto& p= dynamic_cast<const Male<Species>&>(m); // will throw if Species does not match
Species::doMate(p);
} catch ( exception& e) {
cerr << "[MATING ERROR] You filthy animals, stay within your own species!" << endl;
}
}
};
class Bee {
public:
static void doMate(const Male<Bee>& p) {
cout << "Buzz buzz buzz!" <<endl;
}
};
class Bird {
public:
static void doMate(const Male<Bird>& p) {
cout << "Chirpy chirpy cheep cheep!" << endl;
}
};
int main() {
Female<Bee> queenBee;
Male<Bee> drone;
queenBee.tryMate(drone);
Female<Bird> mamaBird;
Male<Bird> papaBird;
mamaBird.tryMate(papaBird);
queenBee.tryMate(papaBird);
}
"How do I capture the case when males[i] and females[i] are of different species at runtime?":取决于尝试交配是否错误。如果可以尝试但不起作用,您可以在 mate()
函数中执行一些检查,例如StillLearning 建议动态转换论点(这是我的物种吗?)。如果你想更灵活,你可以使用可以提供的检查函子使该测试可配置,例如在动物的构造函数中,或作为 mate()
的可选参数。如果测试结果为阴性,则什么都不做。
如果错误的交配尝试是错误的(因为你的向量设置不当而无法继续),你可以抛出一个异常。
编辑:@Buddy 指向双重分配模式的指针对于与不同物种(它们各自基础的不同子类)进行不同的交配是有用且优雅的,即使只提供了一个指针或对一个基础的引用。双重分派可以通过为所需物种明智地重载 mate_callback()
(或其他任何东西——在原始调用者中实现的函数,由原始参数调用)隐含地包含一个 "check",并具有一个包罗万象的东西为所有其他物种抛出或不做任何事情。双重派遣在道德上也让我高兴,因为它需要在交配时达成共识。
您的描述可能指向答案:
The species at each index match, so if males[i] is a bee, then females[i] is also a bee
涵盖专业
But bees don't mate with birds. How would I achieve that kind of specialization?
问:你是如何保证他们匹配的? A: 你一定同时知道这两个条目的类型!当你知道的时候,只需设置一个命令模式、lambda 等,就可以用这两个条目调用专门的方法已知类型。
最后看看 enable_if 的用法——如果我对上述说法不正确,可以用来解决你的问题。
不认为不同的性别应该是不同的class。想想侏罗纪公园里那些自发改变性别的恐龙。还有性别不明(细菌)或交配与性别无关(蜗牛)更不用说无性繁殖。
所以性别应该只是动物的一个属性 class。
另一件事是,有时甚至在不同的动物类型之间也可以交配。想想马和驴。
所以,这就是我建模的方式:
class Animal {
// incomplete....
bool likes(Animal * other) const;
};
class Mammal : public Animal {
static AnimalList Mate(Mammal *a, Mammal *b);
};
AnimalList Mate(const AnimalList &participants);
现在交配的细节取决于动物 classes。全局 Mate()
可以通过 dynamic_cast
进行广泛区分(例如在细菌、植物、哺乳动物等之间)并调用这些函数,然后(可能在更多 dynamic_cast
ing 之后)调用参与者超负荷的交配功能。
交配失败除外。
Animal::likes
最重要的是兼容性;它可能会因伴侣的健康状况或财务状况等偏好而超载。
您基本上有两个选择:编译时检查和运行-时检查。
编译时
您的 MaleAnimal
和 FemaleAnimal
对象没有带有您提供的签名的 mate
方法。实际上,由于 any 女性不能与 any 男性交配,方法签名
FemaleAnimal::mate(const MaleAnimal& a) const
是一个过于慷慨的承诺。不要承诺你无法实现的事情。
一只FemaleAnimal
应该可以与一只MaleAnimal
同种交配。我们怎么写下来?我们将物种编码为动物的类型。在 C++ 中,这将是一个模板。
template <typename Species>
class Animal ...
template <typename Species>
class MaleAnimal : public Animal<Species> ...
template <typename Species>
class FemaleAnimal : public Animal<Species>
{ ...
void mate (const MaleAnimal<Species>&) const ...
}
这种类型安全是有代价的。您不能将不同物种的代表放在同一个容器中 并且 保留它们的物种信息。每个物种都需要一个不同类型的单独容器。
class AnimalContainer
{ ...
virtual void mateAll() = 0;
};
template <typename Species>
class SpecificContainer : public Container
{
...
std::vector<MaleAnimal<Species>> males;
std::vector<FemaleAnimal<Species>> females;
void mateAll()
{
for (...) females[i].mate(males[i]);
}
}
现在您可以保存一个 AnimalContainer
的容器,每个容器可以保存一个单独的物种。
运行-次
你的 FemaleAnimal
可以尝试与任何 MaleAnimal
交配。尝试可能会失败。这最好用 dynamic_cast
建模:如果检查通过,它 returns 是正确的类型,如果没有,它失败(抛出异常)。
层次结构可以这样修改:
class Animal ...
class GenericMaleAnimal : public Animal ...
class GenericFemaleAnimal : public Animal
{ ...
virtual void tryMate (const GenericMaleAnimal&) const = 0;
};
template <typename Species>
class MaleAnimal : public GenericMaleAnimal ...
template <typename Species>
class FemaleAnimal : public GenericFemaleAnimal
{ ...
virtual void tryMate (const GenericMaleAnimal& m) const
{
// will throw if species don't match
auto& other = dynamic_cast<const MaleAnimal<Species>&>(m);
// Specific mate procedure for given species
Species::doMate(*this, other);
}
};
现在您可以将所有内容保存在同一个容器中,并且您有责任使类型匹配。
新手我目前被这个看似简单的问题困住了。假设我想写一些关于一群动物从此幸福地繁殖的代码。显然,他们都需要一个 mate() 方法,所以我可以像这样定义一个抽象 class:
class FemaleAnimal {
public:
virtual FemaleAnimal mate(const MaleAnimal& a) const = 0;
virtual MaleAnimal mate(const MaleAnimal& a) const = 0;
}
并导出所有不同的物种:
class FemaleBird : public FemaleAnimal {
public:
FemaleBird mate (const MaleBird& b) const;
MaleBird mate (const MaleBird& b) const;
}
class FemaleBee: public FemaleAnimal {
public:
FemaleBee mate (const MaleBee& b) const;
MaleBee mate (const MaleBee& b) const;
}
在 main() 中,我想要两个向量
males = vector<MaleAnimal>
females = vector<FemaleAnimal>
每一个都可能包含鸟类和蜜蜂,并在 运行 时填充。每个索引的物种匹配,所以 如果males[i]是一只蜜蜂,那么females[i]也是一只蜜蜂,所以我可以做
vector<FemaleAnimal> femaleOffspring;
vector<MaleAnimal> maleOffspring;
for (int i=0; i<males.size(); ++i){
femaleOffspring.push_back( females[i].mate(males[i]) );
maleOffspring.push_back( females[i].mate(males[i]) );
}
现在,显然我想要派生的 classes 中的 mate() 方法来实现基础 class 中的方法,但是我必须为动物定义 mate()一般来说,比如
FemaleBee::mate(const MaleAnimal& a) const;
但是蜜蜂不与鸟类交配。我将如何实现这种专业化?这有一些特殊的设计模式吗?我试图研究协方差之类的东西,但这比帮助更令人困惑。
附加问题:当雄性[i] 和雌性[i] 在 运行 时间属于不同物种时,我如何捕获这种情况?
编辑:假设男性和女性来自完全不同的 class 等级制度,并且不能从一个共同的基础 class.
中派生出来编辑:为了完整起见,这里是基于 n.m 的答案的最终解决方案。非常感谢大家!
#include <iostream>
using namespace std;
class GenericMale {
public:
virtual void test() = 0; // just to make the class abstract
};
template <typename Species>
class Male : public GenericMale
{
void test() {};
};
class GenericFemale {
virtual void tryMate (const GenericMale&) const = 0;
};
template <typename Species>
class Female : public GenericFemale
{
public:
virtual void tryMate (const GenericMale& m) const
{
try {
auto& p= dynamic_cast<const Male<Species>&>(m); // will throw if Species does not match
Species::doMate(p);
} catch ( exception& e) {
cerr << "[MATING ERROR] You filthy animals, stay within your own species!" << endl;
}
}
};
class Bee {
public:
static void doMate(const Male<Bee>& p) {
cout << "Buzz buzz buzz!" <<endl;
}
};
class Bird {
public:
static void doMate(const Male<Bird>& p) {
cout << "Chirpy chirpy cheep cheep!" << endl;
}
};
int main() {
Female<Bee> queenBee;
Male<Bee> drone;
queenBee.tryMate(drone);
Female<Bird> mamaBird;
Male<Bird> papaBird;
mamaBird.tryMate(papaBird);
queenBee.tryMate(papaBird);
}
"How do I capture the case when males[i] and females[i] are of different species at runtime?":取决于尝试交配是否错误。如果可以尝试但不起作用,您可以在 mate()
函数中执行一些检查,例如StillLearning 建议动态转换论点(这是我的物种吗?)。如果你想更灵活,你可以使用可以提供的检查函子使该测试可配置,例如在动物的构造函数中,或作为 mate()
的可选参数。如果测试结果为阴性,则什么都不做。
如果错误的交配尝试是错误的(因为你的向量设置不当而无法继续),你可以抛出一个异常。
编辑:@Buddy 指向双重分配模式的指针对于与不同物种(它们各自基础的不同子类)进行不同的交配是有用且优雅的,即使只提供了一个指针或对一个基础的引用。双重分派可以通过为所需物种明智地重载 mate_callback()
(或其他任何东西——在原始调用者中实现的函数,由原始参数调用)隐含地包含一个 "check",并具有一个包罗万象的东西为所有其他物种抛出或不做任何事情。双重派遣在道德上也让我高兴,因为它需要在交配时达成共识。
您的描述可能指向答案:
The species at each index match, so if males[i] is a bee, then females[i] is also a bee
涵盖专业
But bees don't mate with birds. How would I achieve that kind of specialization?
问:你是如何保证他们匹配的? A: 你一定同时知道这两个条目的类型!当你知道的时候,只需设置一个命令模式、lambda 等,就可以用这两个条目调用专门的方法已知类型。
最后看看 enable_if 的用法——如果我对上述说法不正确,可以用来解决你的问题。
不认为不同的性别应该是不同的class。想想侏罗纪公园里那些自发改变性别的恐龙。还有性别不明(细菌)或交配与性别无关(蜗牛)更不用说无性繁殖。
所以性别应该只是动物的一个属性 class。
另一件事是,有时甚至在不同的动物类型之间也可以交配。想想马和驴。
所以,这就是我建模的方式:
class Animal {
// incomplete....
bool likes(Animal * other) const;
};
class Mammal : public Animal {
static AnimalList Mate(Mammal *a, Mammal *b);
};
AnimalList Mate(const AnimalList &participants);
现在交配的细节取决于动物 classes。全局 Mate()
可以通过 dynamic_cast
进行广泛区分(例如在细菌、植物、哺乳动物等之间)并调用这些函数,然后(可能在更多 dynamic_cast
ing 之后)调用参与者超负荷的交配功能。
交配失败除外。
Animal::likes
最重要的是兼容性;它可能会因伴侣的健康状况或财务状况等偏好而超载。
您基本上有两个选择:编译时检查和运行-时检查。
编译时
您的 MaleAnimal
和 FemaleAnimal
对象没有带有您提供的签名的 mate
方法。实际上,由于 any 女性不能与 any 男性交配,方法签名
FemaleAnimal::mate(const MaleAnimal& a) const
是一个过于慷慨的承诺。不要承诺你无法实现的事情。
一只FemaleAnimal
应该可以与一只MaleAnimal
同种交配。我们怎么写下来?我们将物种编码为动物的类型。在 C++ 中,这将是一个模板。
template <typename Species>
class Animal ...
template <typename Species>
class MaleAnimal : public Animal<Species> ...
template <typename Species>
class FemaleAnimal : public Animal<Species>
{ ...
void mate (const MaleAnimal<Species>&) const ...
}
这种类型安全是有代价的。您不能将不同物种的代表放在同一个容器中 并且 保留它们的物种信息。每个物种都需要一个不同类型的单独容器。
class AnimalContainer
{ ...
virtual void mateAll() = 0;
};
template <typename Species>
class SpecificContainer : public Container
{
...
std::vector<MaleAnimal<Species>> males;
std::vector<FemaleAnimal<Species>> females;
void mateAll()
{
for (...) females[i].mate(males[i]);
}
}
现在您可以保存一个 AnimalContainer
的容器,每个容器可以保存一个单独的物种。
运行-次
你的 FemaleAnimal
可以尝试与任何 MaleAnimal
交配。尝试可能会失败。这最好用 dynamic_cast
建模:如果检查通过,它 returns 是正确的类型,如果没有,它失败(抛出异常)。
层次结构可以这样修改:
class Animal ...
class GenericMaleAnimal : public Animal ...
class GenericFemaleAnimal : public Animal
{ ...
virtual void tryMate (const GenericMaleAnimal&) const = 0;
};
template <typename Species>
class MaleAnimal : public GenericMaleAnimal ...
template <typename Species>
class FemaleAnimal : public GenericFemaleAnimal
{ ...
virtual void tryMate (const GenericMaleAnimal& m) const
{
// will throw if species don't match
auto& other = dynamic_cast<const MaleAnimal<Species>&>(m);
// Specific mate procedure for given species
Species::doMate(*this, other);
}
};
现在您可以将所有内容保存在同一个容器中,并且您有责任使类型匹配。