|
|
|
Sponsored Link •
|
|
Advertisement
|
Bill Venners: This past spring I attended a .Net seminar at Microsoft. It was my first visit to Microsoft. I picked up many interesting subtexts, one of which concerned pragmatism versus idealism. One Microsoft manager said to me, "Microsoft is a company of pragmatists." I sensed a somewhat dismissive attitude at Microsoft of people who were too academic or idealistic. At times I got the sense they felt Java tries to be too pure or academic, and that C# is more pragmatic.
One specific example of this pragmatic attitude involved virtual and non-virtual functions. In Java, every instance method is dynamically bound, or virtual in C++ jargon. You can mark an instance method final in Java, in which case subclasses can't override it. But there's really nothing like a non-virtual instance method in Java.
In C++ and C#, you have a choice between virtual and non-virtual member functions.
In both languages, the default is non-virtual. You have to specifically mark the function
with a virtual keyword to achieve dynamic binding. Anders Hejlsberg,
C#'s main designer, claimed that making virtual the default, as it is in Java, is wrong. I'm
curious what you think of that philosophical difference between the Java designers and
the C# designers. What do you think should be the default and why? Also, how would
you suggest designers using C++ and C# choose between virtual and non-virtual
functions?
Scott Meyers: Let me answer the second question first. A non- virtual function is an assertion. It asserts that this method applies to this class and any derived classes that may exist; furthermore, you have to use this implementation. It asserts that if this implementation is not appropriate, you should not inherit from this class. A non-virtual function adds a new constraint to derived classes. It says, you have to do this, and you have to do it this way. If you don't want to do it this way, you can't inherit from me. You're not an IS-A relationship with me anymore.
Bill Venners: That's what final does in Java. When you add final to the method, subclasses can't override it. It's the same constraint.
Scott Meyers: It's exactly the same constraint. Unlike Java, C++ has no language mechanism to enforce that constraint. In Java, if you then try to override a final method in a derived class, the compiler would reject the code. C++ can express the constraint. That's what a non-virtual function is. But C++ doesn't have a language mechanism to enforce the constraint. You can play stupid C++ tricks and enforce it, but no one does that in practice.
A virtual function means the interface to the function applies to this class. By interface, I mean the function name, the types and order of the parameters, the return type, and most importantly, the semantics. A virtual function's interface applies to this class and all derived classes. That will never change. However, the implementation, if one is provided, because it could be a pure virtual function, is a default implementation. It may be overridden if you want to accomplish exactly the same thing in a different way. Because what the function accomplishes is fixed. That's the semantics. You can't play games with the semantics. If you do, you violate the IS-A relationship.
A non-virtual function says, you have to do this and you must do it this way. A virtual function says you have to do this, but you don't have to do it this way. That's their fundamental difference.
Regarding the C# decision that the default should be non-virtual rather than virtual, I can't put words in the designer's mind. I can tell you exactly why it's the default in C++.
Bill Venners: Why is non-virtual the default in C++?
Scott Meyers: Because it's more efficient. Because it's not dynamically bound, right? More important, this means that if you have a class in C++, and all member functions are non-virtual, then you will not have a vtbl (virtual table) or a vptr (virtual table pointer). So the object, as well as your overall application data space, is smaller. The decision to make functions non-virtual by default strongly ties in with the concern for efficiency.
The decision to make functions non-virtual by default in C++ also ties in with the
concern for C compatibility. In C++, certain member functions are automatically
generated, even in structs. You get an assignment operator if you ever do an assignment.
You get a copy constructor if you make a copy. You get a destructor if you ever create an
instance, at least on the stack or statically. And it is important that those functions are
non-virtual, because if they were virtual, then you would change the object's layout and it
would no longer be compatible with C.
Have an opinion about contracts, private data, or minimal and complete interfaces? Discuss this article in the News & Ideas Forum topic,
Meaningful Programming
Resources
Effective C++, Second Edition, by Scott Meyers is at Amazon.com at:
http://www.amazon.com/exec/obidos/ASIN/0201924889/
More Effective C++, by Scott Meyers is at Amazon.com at:
http://www.amazon.com/exec/obidos/ASIN/020163371X/
Effective STL, by Scott Meyers is at Amazon.com at:
http://www.amazon.com/exec/obidos/ASIN/0201749629/
Effective C++ CD, by Scott Meyers is at Amazon.com at:
http://www.amazon.com/exec/obidos/ASIN/0201710919/
Scott Meyer's website contains links to many publications, presentations, books, and other information from Scott:
http://www.aristeia.com/
Images of Persephone, the best dog in the world:
http://www.aristeia.com/Persephone/index_frames.html
|
Sponsored Links
|