Differences between the C++ and the Java object model [closed]
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this questionIn Java it is very easy to serialize objects. In C++ it is only safe(?) to
memcpy
objects as long as they are like C structs (no polymorpism). In C++, if the compiler is able to generate the default (trivial) copy constructor then why can't it generate code for automatic serialization?In Java, only static functions and data members are reachable from the ctor. In C++ I can happily use the non-static members and functions from the ctor.In Java, I can initialize data members inline, in the class. In C++ it is a compile error.
In Java I can initialize
final
members in the ctor. In C++ I have to do the initialization of theconst
members in the initialization list. 开发者_JS百科In C++, when control reaches the body of the ctor, all the members ctor has run, right?In Java a ctor can call another ctor. In C++ we cannot do that.
In Java, the
this
is not valid until after the ctor returns (escape of thethis
reference, a bug in multi-threading). When isthis
valid in C++? Thethis
can easily escape both in C++ and in Java: registering a not yet constructed object to Listeners in the ctor (observer pattern).In Java, I cannot make a public function of the base class private in the derived class. I was shocked to see that in C++ is OK and even useful.
Could anyone give a short explanation for these differences?
Update. Trying to collect the answers got so far.
Boost has some serialization-like support. (Tony)
Even though I messed up this point, Alf P. Steinbach gave an interesting example.
C++0x will support much more practical initialization than C++98. (Alf P. Steinbach) #3 will be legal in C++0x (Ken Bloom)
The data members declared in the constructor's own class are guaranteed to have been fully constructed by the time the constructor's {body} starts executing. (c++-faq-lite)
C++0x will allow constructors to call other peer constructors (Wikipedia, C++0x)
C++03 considers an object to be constructed when its constructor finishes executing (Wikipedia).
Things like access control have little to do with the object model: that's a feature of the access control system which is a compile time feature. (Yttrill)
In Java it is very easy to serialize objects. In C++ it is only safe(?) to memcpy objects as long as they are like C structs (no polymorpism).
Java is an interpreted language (or more recently, as Billy comments, JIT compiled), so it has no choice but to carry around metadata baggage of every data type in the program at run time. Between the interpreter, VM, optional compiler and metadata overheads, Java programs need a lot of memory. C++ is a compiled language, where many of the decision Java makes are made once at compile time, and the metadata isn't around for interpretation to guide serialisation at run-time. In general, the metadata isn't exposed even at compile time, probably because different compiler vendors model the program quite differently, and they haven't collectively negotiated a suitable representation. It's also considerable work. Bjarne Stroustrup has some papers on how to expose such information, but it's not even planned for C++0x. Meanwhile, with a little manual markup C++ programs can serialise objects - see boost for a good implementation.
In Java, I can initialize data members inline, in the class. In C++ it is a compile error.
Each C++ constructor provides a complete, independent view of how the object will be initialised. If worthwhile, common construction steps can be factored into a support routine, but the call to that is still visible in the constructor. Having to inspect various assignments scattered through the class would delocalise that, though it can certainly be convenient. Much of a muchness here, I'd hazard.
In Java I can initialize final members in the ctor. In C++ I have to do the initialization of the const members in the initialization list.
This reflects the idea that const members are created with their one and only value, and do not transition from some indeterminate or null state to an initialised state.
In Java a ctor can call another ctor. In C++ we cannot do that.
Yes, it's a little annoying in C++ sometimes - particularly for references and consts that need to be in the initialiser list, but you can factor other common construction steps into a support function. I think C++'s position reflects the job of a constructor in constructing bases, members, pointers to virtual dispatch tables etc. - it's not necessarily possible at the machine code level to call into one constructor from another and have just the right steps execute. The compiler could be required to generate a second callable-from-another-constructor version, or inline the right parts of the called constructor, but that's kind of hidden bloat.
In Java, I cannot make a public function of the base class private in the derived class. I was shocked to see that in C++ is OK and even useful.
Given you acknowledge it's useful, maybe Java should add it.
Could anyone give a short explanation for these differences?
Well, I tried.
Many of these are covered by design philosophy.
In a lot of cases, Java disallowed practices that were dangerous or didn't make sense most of the time. For instance, you cannot call a method from a constructor because there is no guarantee that the members have been initialized until the constructor exits. Java tries not to let any reference to an object escape until the constructor has completed.
In C++ they generally assume the programmer knows all the potential ramifications of his actions and lets them do whatever they want.
Also Java threw away C backwards compatibility, you can't take a C file and hand it to a Java compiler.
The rest of the differences are probably just cases where C++ was designed before Java and Java learned some stuff from problems people had with C++. The same way C# has cleaner syntax or more functionality than Java in some cases, because they learned from Java.
They're different languages. <- how's that for short.
The OP's title is a bit confused. Things like access control have little to do with the object model: that's a feature of the access control system which is a compile time feature. The sequencing of object construction is part of the object model. The key difference between C++ and Java is actually that Java is garbage collected, whereas C++ is not.
Another core differences is that Java uses archaic crud for polymorphism, whereas C++ uses a broken template concept: in C++ templates are compile time and don't impact the object model, in Java polymorphism is hacked around what in C++ is called a dynamic cast.
Another major core difference is that Java supports threads whereas in C++ you have to use a library of some kind.
There are lower level type system differences too: Java has minimal static typing but otherwise is basically a dynamic language. C++ on the other hand is almost entirely a statically typed language, with the exception of exceptions ..:)
A bit of 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();
}
}
Output:
Derived.sayHello says... Exception in thread "main" java.lang.NullPointerException at Derived.sayHello(conscall.java:28) at Base.(conscall.java:16) at Derived.(conscall.java:32) at ConsCallDemo.main(conscall.java:41)
The problem is that in Java the calls go down to the class that the object is instantiated from, even before that instance has been initialized.
C++ guards against this problem by dynamically adjusting the type of the object during construction and destruction. In C++ the object is of type Base
when the Base
constructor executes. Thus, calls from within the Base
constructor behave is if the object was instantiated from class Base
, regardless of actual instantiation class.
Example of that, just translating the Java code above to C++:
#include <iostream>
#include <string>
#include <vector>
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;
}
Output:
Base::sayHello says... Base hello
One way to obtain the probably intended effect in C++, which also suggests how to do it (properly) in Java:
#include <iostream>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
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;
}
Output:
Derived::sayHello says... Derived hello
Regarding your other points, it's too much to discuss here. Just be aware that C++0x will support much more practical initialization than C++98. And also that the formal side of your point 7 is a bit contested, there's a Defect Report about it (it was not allowed in pre-standard C++ as described by the Annotated Reference Manual, but it was apparently intended to be allowed in standard C98, however, the wording is a bit self-contradictory).
Cheers & hth.,
About 7:
In Java, I cannot make a public function of the base class private in the derived class. I was shocked to see that in C++ is OK and even useful.
If you were able to hide a superclass method that would not guarantee your method can not be called by a user. One could always cast to the superclass and call the method, since all non-static methods in Java are as C++ virtual methods your subclass method would actually be called.
It would work with static methods but I don't think overriding static methods is a good practice.
The need to hide methods in subclasses usually means bad design, but if you really need to, you should throw an exception. The down side of this is that it's runtime, but when a call occurs it WILL crash. Whereas in C++, if you hide a method but cast to the parent class, if its virtual the method will actually get called and if it's statically linked the parent class method will get called, both are probably not what you want.
精彩评论