我正在尝试使类的每个实例(此处名为Caller
)具有另一个类的实例(Target
)。关键是第二个类有很多孩子,我需要能够随意在其中进行Caller
类切换。我尝试了几种方法,但没有一种方法给了我任何理想的结果。目前的代码:
class Target
{
public:
virtual void do_something()
{ log_message("NO!"); }
};
class TargetChild : public Target
{
public:
virtual void do_something()
{ log_message("YES!"); }
};
class Caller
{
private:
Target target;
public:
void call_target()
{ target.do_something(); }
void set_target(Target set_target)
{ target = set_target; }
};
int main( int argc, const char* argv[] )
{
TargetChild targetChild;
Caller caller;
caller.call_target();
caller.set_target(targetChild);
caller.call_target();
}
日志文件中的结果是“NO!YES!”但它写了NO!两次。我真的不明白它有什么问题。
答案 0 :(得分:6)
您无法在C ++中更改对象的类型。您只能创建,销毁,复制和(在C ++ 11中)移动数据。在这种情况下,您已将子类对象中的数据复制到基类对象。这复制了空子集的空子集。您观察到的问题称为"切片。"
你想要的是一个包含可以改变的函数指针的成员。 virtual
为您提供一个函数指针,但无法更改。试试std::tr1::function
,boost::function
或C ++ 11 std::function
。
或者,如果您真的想要使用virtual
路线,请使用Target *
指针。最好使用智能指针类,例如unique_ptr< Target >
(再次是Boost / TR1 / C ++ 11)或std::auto_ptr< Target >
(老式的C ++ 03)。您也可以使用new
和delete
自行完成,但这些代码并不能真正发挥作用,只适合进行一些教育修补。
答案 1 :(得分:1)
您的代码遇到object slicing。即使targetChild
中的main
是TargetChild
,它也会按值传递给Caller::set_target
,这意味着它会被复制到类型为Target
的局部变量。要解决一般的对象切片,必须使用pass-by-reference或使用指针。
在这种情况下,由于您希望Caller
访问Caller::set_target
方法之外的传递对象,因此您应该使用指针。单独引用不起作用,因为虽然您可以使Caller::target
成为引用,但您无法更改它引用的对象。
指针引入了内存管理问题,因为您必须确保TargetChild
已取消分配(否则程序会发生内存泄漏),但不会太快(这会导致访问冲突,最有可能导致程序崩溃)。 boost库有各种smart pointer类,可以使这更容易。如果在任何一个时间点只有一个其他类应该拥有TargetChild
实例,那么也可以使用C ++标准auto_ptr
类。
答案 2 :(得分:0)
使用指针代替。有关多态性的更多信息:http://www.cplusplus.com/doc/tutorial/polymorphism/
...
class Caller
{
private:
Target* target;
public:
Caller() { target = NULL; }
void call_target() { if (target != NULL) target->do_something(); }
void set_target(Target* set_target) { target = set_target; }
};
int main( int argc, const char* argv[] )
{
....
caller.set_target(&targetChild);
...
}
答案 3 :(得分:0)
在set_target中,您按值传递targetChild,在那里您没有机会调用TargetChild :: do_something。您需要扩展调用者以包含对当前目标的引用(或指针),引用(或指针)将保留有关原始TargetChild的信息,然后编译器仍将调用TargetChild :: do_something。
答案 4 :(得分:0)
使用界面,ITarget并随意切换它们 ITarget
virtual void do_something() { log_message("NO!"); }
将所有内容视为ITarget类型
这是一种处理它的巧妙方法。
我的c ++生锈了:)