[comp.lang.c++] private base classes

linton@sgi.com (Mark Linton) (02/09/91)

In article <64173@brunix.UUCP>, sdm@cs.brown.edu (Scott Meyers) writes:
|> In article <1174@zinn.MV.COM> mjv@objects.mv.com (Michael J. Vilot) writes:
|> | I agree with Keith, and with Tom Cargill, that private base classes can be
|> | transformed into class members.  There may be some minor performance
|> | differences between the two, but I suspect on average it's a wash.  
|> 
|> No no no.  You lose virtual functions when you turn inheritance into member
|> containment.  There is a conceptual distinction between the following:
|> 
|>     A has a member B             ==> A "has a" B (or A "contains a" B)
|>     A publicly inherits from B   ==> A "is a" B
|>     A privately inherits from B  ==> A "is implemented in terms of a" B
|> 
|> Private inheritance is a convenient way to share code when there is no
|> other conceptual relationship between two classes.  I usually call this
|> "code stealing."  When stealing the code in another class, you often have
|> to do some class specific stuff; this is where virtual functions come in.
|> The member functions in the (private) base class can call virtual functions
|> to do the customization.  This won't work with member objects, because they
|> have no way of knowing what they're a part of.

The case you refer to is pretty pathological.  I'll use the names Base and Derived
instead of your B and A.  Since Derived privately inherits from Base,
the only place you can call a Base method from a Derived object is in Derived itself.
Then Base::method calls a virtual on Base that Derived redefines and you
end back in a Derived method.  Example code is:

    class Base { virtual void f(); void g(); };
    class Derived : private Base { virtual void f(); void h(); };

    void A::f() { }
    void A::g() { shared_before_stuff(); f(); shared_after_stuff(); }
    void B::f() { ... }
    void B::h() { g(); }

Note that B::f() can't be called directly (except from another B method, of course).

You refer to private inheritance as "convenient", but I have never needed
the circular control flow above.  We don't use it at all in InterViews
(about 100K lines of C++).  Most examples of code sharing I have seen
are better implemented with either separate types or templates
 (or the macro equivalent).  If you have a real example of "convenient" private inheritance, I'd be interested in seeing it.

chip@tct.uucp (Chip Salzenberg) (02/13/91)

According to linton@sgi.com (Mark Linton):
> If you have a real example of "convenient" private inheritance,
> I'd be interested in seeing it.

In a real application I have two classes, SqlStmt and SqlCursor.
SqlStmt is a private base class of SqlCursor.  I would have made it
public, but to maintain internal consistency I can allow only one
member function of SqlStmt public, namely, SqlStmt::in().  However,
SqlStmt::in() is actually several overloaded functions.  Thus the
declaration:

    class SqlCursor : private SqlStmt {
      public:
        SqlCursor();
        ~SqlCursor();

        SqlStmt::in;

        // other declarations...
    };

This declaration conveniently makes all the SqlStmt::in() functions
public without needing to name them all.

The use of inheritance instead of a SqlStmt member variable also
implies that the member functions of SqlCursor can call member
functions of SqlStmt without specifying "member.func()".

Both of these aspects are convenience wins.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
 "I want to mention that my opinions whether real or not are MY opinions."
             -- the inevitable William "Billy" Steinmetz