[英]Polymorphism in C++ vs Java
我正在将一些Java代码转换为C ++,并且希望保持类结构相似。 但是,我遇到了以下问题,我不知道该如何解决。 我在Java中执行此操作:
public class Mother {
protected Father make;
public Mother(){
make = maker();
make.print(); };
public Father maker(){
return new Father();};}
public class Daughter extends Mother {
public Daughter(){
super();}
@Override
public Father maker(){
return new Son();};}
public class Father {
public void print(){
System.out.println("I am the Father!\n");}}
public class Son extends Father {
@Override
public void print(){
System.out.println("I am the son!\n");};}
public static void main(String[] args) {
Daughter dot = new Daughter();
}
会产生: 我是儿子! 而:
class father{
public:
virtual void print(){
std::cout << "I am the father!\n";
}; };
class son: public father{
public:
virtual void print(){
std::cout << "I am the son!\n";
};};
class mother{
protected:
father *make;
public:
mother(){
make = maker();
make->print();
};
virtual father *maker(){
return new father();
};};
class daughter: public mother{
public:
daughter(): mother() {
};
virtual father *maker(){
return new son();
};};
int main(int argc, const char * argv[]) {
daughter *d = new daughter();
会产生我是父亲! 。 如何使C ++代码产生与Java代码相同的结果? 谢谢。
Daughter
的构造函数调用Mother
构造函数, Mother
构造函数调用maker()
。 至少在C ++中,由于Daughter
构造不完整,因此在这一点上仅将该对象视为Mother
。 因此,调用了Mother::maker()
,所以这是在做正确的事情。 但是,出于这些原因,通常在构造过程中调用虚拟函数被认为是强烈的代码味道。
在Java中,显然,即使在构造过程中,始终会调用子类重写,因此,Java中的构造函数绝不应调用可重写方法。 这样做可能导致不确定的行为。 还有就是这是一个非常不错的解释在这里 。
在Java中使用AFAIK时,在构造函数中调用(非最终)方法(这是可以在派生类中重写的方法)是唯一不好的样式。 C ++始终会调用实际类的版本,而不是被覆盖的版本。
您可以通过将对象传递给构造函数来解决此问题吗?
您不应该从基类构造函数中调用虚函数-派生类的vtable尚未链接到,因此您将始终结束调用基类的函数。 您也不应该在Java中真正执行此操作,因为它将调用正确的函数,但尚未实例化最多派生的类-这可能导致未定义的行为。 最终,由于不同的原因,两种语言都是错误的。
解决此问题的一种方法是让派生类将可能虚拟调用的结果传递给基类:
daughter(): mother(new son) { }
以便:
mother() : make(new father) { make->print(); }
mother(father * m) : make(m) { make->print(); }
通过委派构造函数,这变得更加容易:
mother()
: mother(new father)
{ }
mother(father* m)
: make(m)
{
make->print();
}
在C ++中,从基本构造函数调用虚拟函数不会调用更多派生的实现。 原因是,对于BASE
类型的构造函数,类型是BASE
,即使从派生类DERIVED
调用了该构造函数。 因此,虚拟函数表仍在构建中,直到DERIVED
构造函数完成执行后,才指向更派生的实现。
Java(和C#)与C ++的不同之处在于,您可以从基本构造函数中调用虚拟函数,并且它将调用派生程度最高的实现。 但是,由于派生程度最高的构造函数尚未运行,因此该对象可能处于未定义状态,这就是为什么不建议从构造函数中调用虚函数的原因。
关于如何解决它,您可以添加一个initialize
方法,该方法在创建实例后调用。 由于此时将完全构建对象,因此它将调用正确的虚函数(所有语言)。
创建派生对象时,它首先调用Base的构造函数。 在执行Base的构造函数(Mother)时,此对象还不是Derived(daughter)类型; 它的类型仍然仅仅是Base(母亲)。有关详细信息, 请阅读此文章: http : //www.parashift.com/c%2B%2B-faq-lite/calling-virtuals-from-ctors.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.