假设我有4个类:
- 父母
- 儿童1
- 儿童2
- 儿童3
所有子级都是类父级的后代。
在我的玩具程序中,我必须为每个孩子创建一个单独的变量,然后处理需要处理的内容。 但是我希望有一个父类型的变量,可以转换为子变量。
这是我当前的解决方案:
int main(int argc, char* argv[]) {
Child1 c1 = Child1();
Child2 c2 = Child2();
Child3 c3 = Child3();
switch(arg[1]) {
case CHILD1:
c1.start();
break;
case CHILD2:
c2.start();
break;
case CHILD3:
c3.start();
break;
}
return 0;
}
以下是我希望获得的解决方案类型:
int main(int argc, char* argv[]) {
Parent p = Parent();
switch(arg[1]) {
case CHILD1:
(Child1)p.start();
break;
case CHILD2:
(Child2)p.start();
break;
case CHILD3:
(Child3)p.start();
break;
}
return 0;
}
我知道上面的代码不正确。 但我认为它正确地传达了我想要表达的内容。 我不想浪费内存来创建从未使用过的对象。
想法一:在交换机中创建项目
int main(int argc, char* argv[]) {
switch(arg[1]) {
case CHILD1:
{
Child1 p;
p.start();
break;
}
case CHILD2:
{
Child2 p;
p.start();
break;
}
case CHILD3:
{
Child3 p;
p.start();
break;
}
}
return 0;
}
似乎很多代码都是复制粘贴的,对吗? 右。 如果Parent
具有虚拟析构函数和虚拟start
方法,则可以使用工厂模式,从而最大程度地减少重复。
std::unique_ptr<Parent> child_factory(char*) {
switch(arg[1]) {
case CHILD1: return std::make_unique<Child1>();
case CHILD2: return std::make_unique<Child2>();
case CHILD3: return std::make_unique<Child3>();
default: throw std::runtime_error("invalid child type");
}
}
int main(int argc, char* argv[]) {
std::unique_ptr<Parent> p = child_factory(arg[1]);
p->start();
return 0;
}
如果这些对象不需要很长时间,那么你可以简单地做:
switch(arg[1]) {
case CHILD1:
{
Child1 c1;
c1.start();
break;
}
case CHILD2:
{
Child2 c2;
c2.start();
break;
}
case CHILD3:
{
Child3 c3;
c3.start();
break;
}
}
另一方面,如果您需要根据Parent
类型保留某些内容更长时间,则需要查看分配给基类型指针的动态分配对象。在这种情况下,您可能希望start
方法virtual
。然后你可以做:
Parent* p = NULL;
switch(arg[1]) {
case CHILD1:
p = new Child1;
break;
case CHILD2:
p = new Child2;
break;
case CHILD3:
p = new Child3;
break;
}
p.start();
// more stuff
delete p;
当然,这里还有很大的改进空间,例如处理arg[1]
与任何情况都不匹配的情况。此外,最佳做法是使用智能指针进行p
以便更可靠地处理释放。
强制转换语法不是(Child)p.start()
,但它是((Child)p).start()
。前者表示(Child) (p.start())
,因此它将强制转换 start 返回的值,而不是 P。
无论如何,根本不需要强制转换,因为它们都派生自"父级"。阅读有关虚拟方法的信息。使用它们,您可以执行以下操作:
Parent p = ...
p.start(); // the virtual method 'start' takes care of calling the right code
但是#1:没有免费的午餐。虚拟方法确保当你在父级上调用"start"时调用子方法,但仍然必须根据arg[1]选择正确的子方法1/子2/子3。
Parent p = chooseTheChild( arg[1] ); // you need to write it..
p.start(); // the virtual method 'start' takes care of calling the right code
但是#2:它不适用于普通父级。虚拟方法适用于指针。
Parent* p = chooseTheChild( arg[1] ); // you need to write it..
p->start(); // the virtual method 'start' takes care of calling the right code
现在你可以编写"选择"函数了。又没有免费的午餐,还是一个开关。由于需要指针,因此您必须new
孩子:
class Parent
{
public:
~Parent() { }
virtual void start() = 0;
};
class Child1 : public Parent
{
public:
virtual void start() { std::cout << "start1!" << std::endl; }
};
class Child2 : public Parent
{
public:
virtual void start() { std::cout << "start2!" << std::endl; }
};
class Child3 : public Parent
{
public:
virtual void start() { std::cout << "start3!" << std::endl; }
};
Parent* chooseTheChild( ChildType ttt)
{
switch(ttt) {
case CHILD1: return new Child1();
case CHILD2: return new Child2();
case CHILD3: return new Child3();
}
}
.. somewhere ...
Parent* p = chooseTheChild( arg[1] );
p->start(); // the virtual method 'start' takes care of calling the right code
delete p; // new'ed? delete it when finished!
这段代码是一个草图,并不意味着 100% 正确和干净,但应该给你一些可以玩的东西。例如,显然需要智能指针而不是普通指针。等。
显而易见的解决方案是在需要时创建孩子。如果您愿意,可以使用函数模板来消除重复代码:
#include <iostream>
struct Child1{
void start() { std::cout << "Start Child1n"; }
};
struct Child2{
void start() { std::cout << "Start Child2n"; }
};
struct Child3{
void start() { std::cout << "Start Child3n"; }
};
template<typename Child>
void start() {
auto child = Child();
child.start();
}
int main(int /*argc*/, char* argv[]) {
auto type = std::string(argv[1]);
if (type == "CHILD1")
start<Child1>();
else if (type == "CHILD2")
start<Child2>();
else if (type == "CHILD3")
start<Child3>();
}
在这种特殊情况下,如果您不想,则不需要virtual
方法,但在更复杂的情况下,您可能希望有一个返回指向Parent
的(智能)指针的工厂,并在Parent
上具有虚拟析构函数和启动方法。
对不起,但这种明确的想法继承是错误的。 父母和孩子之间的关系可以想到:child_object是一个parent_object,这意味着child_object也可以用作parent_object,即可以铸造,称为,...作为一个,反之亦然!
Mooing Duck的第二个例子是好东西。