2011-11-17 75 views
3

我试图让每个类的实例(在这里命名为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!两次。我真的不知道它有什么问题。

回答

6

您不能在C++中更改对象的类型。您只能创建,销毁,复制和(在C++ 11中)移动数据。在这种情况下,您已将数据从子类对象复制到基类对象。这复制了一个空子集的空子集。你观察到的问题叫做“切片”。

可能你想要的是一个包含可以改变的函数指针的成员。 virtual给你一个函数指针,但它不能被改变。试试std::tr1::functionboost::function或C++ 11 std::function。或者,如果您确实想要使用virtual路由,请使用Target *指针。最好使用智能指针类,例如unique_ptr<Target>(又是Boost/TR1/C++ 11)或std::auto_ptr<Target>(老式的C++ 03)。你也可以用newdelete自己来完成,但是这样的代码实际上并不能很好地工作,只适用于一些教育修补。

+0

非常感谢。由于我已经包含它,可能会尝试Boost方式。 –

+0

使用Boost的智能指针解决。非常感谢! –

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); 
    ... 
} 
+0

垃圾,我忍住了。 :)) – Tudor

+0

您正在调用未定义的行为,因为第一次调用'call_target'发生在单位化指针上(假设您将其余代码保留在'main'中)。 –

+0

将指针检查添加到'call_target'中... –

0

在set_target你是按值传递targetChild,有你正在失去致电TargetChild :: do_something的机会。您需要扩展调用方以包含对当前目标的引用(或指针),引用(或指针)将保留有关原始TargetChild的信息,然后编译器仍将调用TargetChild :: do_something。

1

您的代码患有object slicing。尽管main中的targetChildTargetChild,但它的值被传递到Caller::set_target,这意味着它被复制到类型为Target的局部变量。一般来说,要绕过对象切片,必须使用传递引用或使用指针。

在这种情况下,由于您希望Caller访问Caller::set_target方法之外的传递对象,因此应该使用指针。单独的参考将不起作用,因为尽管您可以将Caller::target作为参考,但您无法更改其引用的对象。

指针引入了内存管理问题,因为您必须确保TargetChild被释放(否则程序有内存泄漏),但不会太早(这会导致访问冲突,很可能会导致程序崩溃)。助推库有各种类型的smart pointer类,以使这更容易。如果在任何时间点只有一个其他类应拥有TargetChild实例,则也可以使用C++标准auto_ptr类。

+0

感谢您提供的链接,它们也非常有帮助。 –

0

使用接口,ITarget和随意切换 他们ITarget

virtual void do_something()  { log_message("NO!"); } 

对待一切,ITarget

类型的处理它这是一个巧妙的方法。

我的C++很生锈:)