Xia*_*Jia 15 c++ java virtual-functions object-lifetime vptr
在Java中:
class Base {
public Base() { System.out.println("Base::Base()"); virt(); }
void virt() { System.out.println("Base::virt()"); }
}
class Derived extends Base {
public Derived() { System.out.println("Derived::Derived()"); virt(); }
void virt() { System.out.println("Derived::virt()"); }
}
public class Main {
public static void main(String[] args) {
new Derived();
}
}
Run Code Online (Sandbox Code Playgroud)
这将输出
Base::Base()
Derived::virt()
Derived::Derived()
Derived::virt()
Run Code Online (Sandbox Code Playgroud)
但是,在C++中,结果是不同的:
Base::Base()
Base::virt() // ? Not Derived::virt()
Derived::Derived()
Derived::virt()
Run Code Online (Sandbox Code Playgroud)
(有关C++代码,请参阅http://www.parashift.com/c++-faq-lite/calling-virtuals-from-ctors.html)
什么导致Java和C++之间的这种差异?这是vtable初始化的时间吗?
编辑:我确实理解Java和C++机制.我想知道的是这个设计决定背后的见解.
Kon*_*lph 14
这两种方法都显然存在缺陷:
this
正常使用的方法,因为它的成员尚未初始化.为什么每种语言都做它所做的事情是一个悬而未决的问题,但两者都可能声称是"更安全"的选择:C++的方式阻止了未初始成员的使用; Java的方法允许在类的构造函数(这是一个完全有效的用例)内的多态语义(在某种程度上).
Che*_*Alf 11
那么你已经链接到常见问题解答的讨论,但这主要是面向问题,而不是进入理由,为什么.
简而言之,它适用于类型安全.
这是C++在类型安全方面胜过Java和C#的少数几种情况之一.;-)
当您创建一个类时A
,在C++中,您可以让每个A
构造函数初始化新实例,以便所有关于其状态的常见假设(称为类不变量)成立.例如,类不变量的一部分可以是指针成员指向某些动态分配的内存.当每个公开可用的方法保留类不变量时,它保证在每个方法的入口处也保持,这极大地简化了事情 - 至少对于精心选择的类不变量!
在每种方法中都不需要进一步检查.
相比之下,使用两阶段初始化(例如在Microsoft的MFC和ATL库中),当调用方法(非静态成员函数)时,您永远无法确定是否所有内容都已正确初始化.这与Java和C#非常相似,只是在那些语言中缺少类不变保证来自这些语言仅仅是启用但不主动支持类不变量的概念.简而言之,从基类构造函数调用的Java和C#虚方法可以在尚未初始化的派生实例上调用,其中(派生)类不变量尚未建立!
因此,这种对类不变量的C++语言支持非常棒,有助于消除大量的检查和许多令人沮丧的令人困惑的错误.
但是,在基类构造函数中执行派生类特定初始化会有点困难,例如在最顶层的GUI Widget
类构造函数中执行常规操作.
FAQ项目"好的,但有没有办法模拟这种行为,好像动态绑定在我的基类的构造函数中对这个对象起作用?".
有关最常见情况的更全面处理,请参阅我的博客文章"如何通过使用零件工厂避免后期构建".
无论它是如何实现的,语言定义所说的都应该发生变化.Java允许您在尚未完全初始化的派生对象上调用函数(它已被零初始化,但其构造函数尚未运行).C++不允许这样做; 直到派生类的构造函数运行,没有派生类.
归档时间: |
|
查看次数: |
4787 次 |
最近记录: |