Re: [ROOT] TVector3 and TLorentzVector

From: George A. Heintzelman (gah@bnl.gov)
Date: Tue Oct 10 2000 - 22:02:43 MEST


> >>>>> "GAH" == George Heintzelman <gah@bnl.gov> writes:
> 
>     >> > TVector3 and TLorentzVector have been left unchanged since
>     >> your mail > and the discussion following it. These two classes
>     >> were supposed to be in > phase with the same classes in
>     >> CLHEP. It is my understanding that the CLHEP > maintainers were
>     >> against your proposal (from some private emails!).
> 
>     >> I think allowing more powerful and understandable code to be
>     >> written is far more important than saving a few bytes here and
>     >> there, but anyway.
> 
>     GAH> However, when you build a library, this is completely
>     GAH> different. Since you're building it (hopefully) for general
>     GAH> use and re-use, you do not have any constraint on the
>     GAH> application people will eventually put it to, and so you
>     GAH> should make it as lean and mean as possible.
> 
> Yes, but I think the key here is reuse.  Unless there is an extremely
> good justification for *preventing* a class from being inherited, then
> it seems library classes should be designed to encourage it. 

I don't think this is true in general. Reuse takes many forms in C++. 
Inheritance is one, and in the case of objects which are going to be 
base classes of course you should write to deal with it. You'll not 
ever find me arguing about virtual destructors and methods in 
TCollection, or TNode for example.

However, there is also composition. ROOT's libraries currently 
discourage to some level the use of composition, because composite 
objects wind up paying the virtual function table/TObject overhead 
repeatedly. So, for example, the TLorentzVector class is size 56, 
because it pays the TObject size cost of 12 twice in addition to 4 
size-8 meaningful data members -- for a total overhead of 2/3 the data. 
In a different design, this would not need to be the case, even keeping 
the (quite fine) implementation of an internal TVector3 in the 
TLorentzVector, and even if you decide you need the overhead at least 
once in order to get your I/O or whatever right, it's half as much.

Also, let us not forget about the merits of private inheritance, which 
can allow you to get much of the desired code reuse (sometimes more 
easily than with composition), but without having the overheads and 
interface-bending (e.g., why does TObject have a no-op FindObject() 
method?) associated with public inheritance and polymorphism.

> I gather this is not a problem in ROOT since the vector classes derive from
> TObject, but it's pretty short sighted in CLHEP.  And, if a class can
> be inherited then an application that needs special performance can
> start by sub-classing the base class while the application is being
> written and debugged.  Later after it's debugged, through the magic of
> C++ the class's inheritance can be changed to use the extremely
> fast/small/whatever special purpose class.

I disagree with the ordering of changes. It is much, much easier to 
write a wrapper that adds a virtual destructor when you need 
polymorphism, than it is to redo the entire functionality of a class 
without a virtual destructor when you don't want the overheads:

TVirtualClass: private TSomeClass {
	using TSomeClass::foo;
	using TSomeClass::bar;
	virtual ~TVirtualClass() {}
};

This is why the C++ standard library is written with very few virtual 
functions or virtual-destructor'd objects -- the philosophy is that if 
you don't need it, you shouldn't have to have it. IMHO CLHEP is not 
wrong-headed to follow that philosophy.

George Heintzelman
gah@bnl.gov



This archive was generated by hypermail 2b29 : Tue Jan 02 2001 - 11:50:35 MET