C ++和Java对象模型之间的差异

  1. 在Java中,序列化对象非常容易。 在C ++中,只有它们像C结构(没有多态性) memcpy对象安全(?)。 在C ++中, 如果编译器能够生成默认(普通)复制构造函数,那么为什么它不能生成自动序列化的代码?

  2. 在Java中,只能从ctor访问静态函数和数据成员。 在C ++中,我可以愉快地使用ctor中的非静态成员和函数。

  3. 在Java中,我可以在类中初始化内联的数据成员。 在C ++中,这是一个编译错误。

  4. 在Java中,我可以初始化ctor中的final成员。 在C ++中,我必须在初始化列表中初始化const成员。 在C ++中,当控制到达ctor的主体时,所有成员ctor都运行了,对吧?

  5. 在Java中,ctor可以调用另一个ctor。 在C ++中,我们不能这样做。

  6. 在Java中, this在ctor返回之后无效(逃避this引用,multithreading中的错误)。 什么时候在C ++中有效? 这在C ++和Java中都很容易逃脱: 在ctor (观察者模式)中向一个监听器注册一个尚未构造的对象

  7. 在Java中,我不能在派生类中创建基类private的公共函数。 我很惊讶地发现在C ++中它是可行的甚至是有用的。

任何人都可以对这些差异做出简短的解释吗?

更新。 到目前为止,试图收集答案。

  1. Boost有一些类似序列化的支持。 (托尼)

  2. 尽管我搞砸了这一点,但Alf P. Steinbach给出了一个有趣的例子。

  3. C ++ 0x将支持比C ++ 98更实用的初始化。 (Alf P. Steinbach)#3在C ++ 0x中是合法的(Ken Bloom)

  4. 在构造函数的{body}开始执行时,保证在构造函数自己的类中声明的数据成员已完全构造。 (C ++ – FAQ-精简版)

  5. C ++ 0x将允许构造函数调用其他对等构造函数(Wikipedia,C ++ 0x)

  6. C ++ 03考虑在构造函数完成执行时构造的对象(Wikipedia)。

  7. 访问控制之类的东西与对象模型几乎没有关系:这是访问控制系统的一个特性,它是一个编译时function。 (Yttrill)

在Java中,序列化对象非常容易。 在C ++中,只有它们像C结构(没有多态性)才对memcpy对象安全(?)。

Java是一种解释型语言(或者最近,比利评论,JIT编译),所以它别无选择,只能在运行时携带程序中每种数据类型的元数据包。 在解释器,VM,可选编译器和元数据开销之间,Java程序需要大量内存。 C ++是一种编译语言,Java编写的许多决策都是在编译时生成的,并且元数据不是用于解释以在运行时指导序列化。 通常,即使在编译时也不会公开元数据,这可能是因为不同的编译器供应商对程序的建模方式完全不同,并且他们没有集体协商合适的表示。 这也是相当可观的工作。 Bjarne Stroustrup有一些关于如何公开这些信息的论文,但它甚至没有计划用于C ++ 0x。 同时,通过一些手动标记C ++程序可以序列化对象 – 请参阅提升以获得良好的实现。

在Java中,我可以在类中初始化内联的数据成员。 在C ++中,这是一个编译错误。

每个C ++构造函数都提供了一个完整的独立视图,了解如何初始化对象。 如果值得,可以在支持例程中考虑常见的构造步骤,但对构造函数的调用仍然可见。 必须检查分散在课堂上的各种作业会使本地化,尽管它确实很方便。 这里很多,我有危险。

在Java中,我可以初始化ctor中的最终成员。 在C ++中,我必须在初始化列表中初始化const成员。

这反映了const成员使用其唯一值创建的想法,并且不会从某个不确定或null状态转换为初始化状态。

在Java中,ctor可以调用另一个ctor。 在C ++中,我们不能这样做。

是的,有时在C ++中有点烦人 – 特别是对于需要在初始化列表中的引用和consts,但是你可以将其他常见构造步骤考虑到支持函数中。 我认为C ++的位置反映了构造函数在构造基础,成员,虚拟调度表指针等方面的作用。 – 在机器代码级别,不一定能从另一个构造函数调用一个构造函数并且只执行正确的步骤。 编译器可能需要生成第二个callable-from-another-constructor版本,或者内联调用构造函数的正确部分,但这是一种隐藏的膨胀。

在Java中,我不能在派生类中创建基类private的公共函数。 我很惊讶地发现在C ++中它是可行的甚至是有用的。

鉴于您承认它很有用,也许Java应该添加它。

任何人都可以对这些差异做出简短的解释吗?

好吧,我试过了。

其中许多都被设计理念所涵盖。

在很多情况下,Java禁止在大多数情况下危险或没有意义的做法。 例如,您无法从构造函数中调用方法,因为在构造函数退出之前无法保证成员已初始化。 Java尝试在构造函数完成之前不允许对对象的任何引用进行转义。

在C ++中,他们通常认为程序员知道他的行为的所有潜在后果,并让他们做任何他们想做的事情。

此外,Java抛弃了C向后兼容性,您无法获取C文件并将其交给Java编译器。

其余的差异可能只是在Java之前设计C ++并且Java从人们使用C ++的问题中学到一些东西的情况。 在某些情况下,C#具有比Java更清晰的语法或更多的function,因为他们从Java学习。

他们是不同的语言。 < - 简而言之。

OP的标题有点混乱。 访问控制之类的东西与对象模型几乎没有关系:这是访问控制系统的一个特性,它是一个编译时function。 对象构造的顺序对象模型的一部分。 C ++和Java之间的关键区别实际上是Java是垃圾收集,而C ++则不是。

另一个核心差异是Java使用古老的crud来实现多态,而C ++使用了一个破碎的模板概念:在C ++模板中是编译时间并且不影响对象模型,在Java多态中被攻击在C ++中被称为动态强制转换的东西。

另一个主要的核心区别是Java支持线程,而在C ++中你必须使用某种类型的库。

还有较低级别的系统差异:Java具有最小的静态类型,但在其他方面基本上是动态语言。 另一方面,C ++几乎完全是一种静态类型语言,除了例外.. 🙂

一点Java:

 class Base { public static void say( String s ) { System.out.println( s ); } public void sayHello() { say( "Base.sayHello says..." ); say( "Base hello" ); } public Base() { sayHello(); } } class Derived extends Base { private String[] myWords; // = new String[]{ "Derived", "hello" }; public void sayHello() { say( "Derived.sayHello says..." ); say( myWords[0] + " " + myWords[1] ); } Derived() { myWords = new String[]{ "Derived", "hello" }; } } class ConsCallDemo { public static void main( String[] args ) { Derived o = new Derived(); } } 

输出:

 Derived.sayHello说......
线程“main”java.lang.NullPointerException中的exception
        在Derived.sayHello(conscall.java:28)
        在Base。(conscall.java:16)
        在Derived。(conscall.java:32)
        在ConsCallDemo.main(conscall.java:41)

问题是在Java中,调用会转到实例化对象的类,即使在初始化该实例之前也是如此。

C ++通过在构造和销毁期间动态调整对象的类型来防范此问题。 在C ++中, Base构造函数执行时,对象的类型为Base 。 因此,无论实际的实例化类如何,从Base构造函数中调用的行为都是从对象类实例化的。

例如,只需将上面的Java代码翻译成C ++:

 #include  #include  #include  using namespace std; void say( string const& s ) { cout << s << endl; } class Base { public: virtual void sayHello() const { say( "Base::sayHello says..." ); say( "Base hello" ); } Base() { sayHello(); } }; class Derived : Base { private: vector< string > words_; public: void sayHello() const { say( "Derived::sayHello says..." ); say( words_[0] + " " + words_[1] ); } Derived() { words_.push_back( "Derived" ); words_.push_back( "hello" ); } }; int main() { Derived o; } 

输出:

 Base :: sayHello说......
基地你好

在C ++中获得可能预期效果的一种方法,它也建议如何在Java中正确地做到这一点:

 #include  #include  #include  #include  using namespace std; void say( string const& s ) { cout << s << endl; } class Base { protected: struct Greeter { typedef boost::shared_ptr< Greeter > Ptr; virtual void sayHello() const { say( "Base::sayHello says..." ); say( "Base hello" ); } }; private: Greeter::Ptr greeter_; public: void sayHello() const { greeter_->sayHello(); } Base( Greeter::Ptr greeter = Greeter::Ptr( new Greeter ) ) : greeter_( greeter ) { sayHello(); } }; class Derived : Base { protected: struct Greeter : Base::Greeter { vector< string > words_; virtual void sayHello() const { say( "Derived::sayHello says..." ); say( words_[0] + " " + words_[1] ); } Greeter() { words_.push_back( "Derived" ); words_.push_back( "hello" ); } }; public: Derived() : Base( Greeter::Ptr( new Greeter ) ) {} }; int main() { Derived o; } 

输出:

 Derived :: sayHello说......
派生你好

关于你的其他观点,在这里讨论太多了。 请注意,C ++ 0x将支持比C ++ 98更实用的初始化。 而且你的第7点的正式方面有点争议,还有一个关于它的缺陷报告(在标注的参考手册中描述的预标准C ++中不允许这样做,但它显然是允许在标准C98中使用但是,措辞有点自相矛盾。

干杯&hth。,

大约7:

在Java中,我不能在派生类中创建基类private的公共函数。 我很惊讶地发现在C ++中它是可行的甚至是有用的。

如果您能够隐藏一个不能保证用户无法调用您的方法的超类方法。 总是可以强制转换为超类并调用该方法,因为Java中的所有非静态方法都是C ++虚方法,实际上会调用子类方法。

它可以使用静态方法但我不认为重写静态方法是一种很好的做法。

在子类中隐藏方法的需要通常意味着糟糕的设计,但如果你真的需要,你应该抛出exception。 这样做的缺点是它是运行时,但是当一个调用发生时它会崩溃。 而在C ++中,如果你隐藏一个方法但是强制转换为父类,如果它的虚拟方法实际上会被调用,如果它是静态链接的,那么父类方法将被调用,两者都可能不是你想要的。