privatising public member functions in derived classes.

V

Victor Bazarov

Firkraag said:
#include <iostream>
using std::cout;

struct A {
~virtual A(){}
virtual void foo(){ cout << "aaa\n"; }
};

struct B : A {
private:
virtual void foo(){ cout << "bbb\n"; }
};

void bar(A& ab)
{
ab.foo();
}


int main()
{
A a;
B b;
bar(b)
}

The above code works properly. As far as I know LSP is not being
violated here. So why FAQ lite says this:
http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.1

Compile this version of 'main' instead:

template<class T> void callFoo(T &t)
{
t.foo();
}

int main()
{
B b;
callFoo(b);
}

Now, the "duck typing" doesn't work, does it? LSP or not, what is
the point of hiding 'foo' in 'B'? If you didn't intend to call the
'foo' member directly, you ought to make it private and keep it
private:

struct A {
private:
virtual void foo() { cout << "aaa\n"; }
public:
void doFoo() { foo(); }
};

struct B : A {
private:
virtual void foo() { cout << "bbb\n"; }
};

V
 
W

werasm

Now, the "duck typing" doesn't work, does it? LSP or not, what is
the point of hiding 'foo' in 'B'? If you didn't intend to call the
'foo' member directly, you ought to make it private and keep it
private:

struct A {
private:
virtual void foo() { cout << "aaa\n"; }
public:
void doFoo() { foo(); }
};

struct B : A {
private:
virtual void foo() { cout << "bbb\n"; }
};

To me this also seems a little pointless. If I want to convey
to clientA that he may only call foo (or doFoo, for that
matter), nothing stops client B from calling doFoo().

What you are showing here above allows doFoo to dictate
the order of the calling (template method) if there is
an order, but it does not help with indicating which
client is supposed to use the applicable interface.

This however does:

class A
{
friend class ClientOfA;
virtual void foo() = 0;
//...
};

or...

class A
{
friend class ClientOfA;
void doFoo(){ /*..call foo etc...*/ }
virtual void foo() = 0;
};

class B{}; //All above private.
 
V

Victor Bazarov

werasm said:
To me this also seems a little pointless. If I want to convey
to clientA that he may only call foo (or doFoo, for that
matter), nothing stops client B from calling doFoo().

But that's the whole point! The client of A or B or any other
derived classes will have to always call doFoo() instead of
'foo' (which can have different behaviour due to static or
dynamic binding, IOW when calling through a pointer or directly
for an object).
What you are showing here above allows doFoo to dictate
the order of the calling (template method)

What template method? What are you talking about?
if there is
an order, but it does not help with indicating which
client is supposed to use the applicable interface.
Huh?


This however does:

Does what, exactly?
class A
{
friend class ClientOfA;
virtual void foo() = 0;
//...
};

or...

class A
{
friend class ClientOfA;
void doFoo(){ /*..call foo etc...*/ }
virtual void foo() = 0;
};

class B{}; //All above private.

The idiom of having all virtual interface private (pure or not)
and having a public interface that just forwards the calls to the
virtual function, is well known and commonly accepted in the
industry. The main advantage is that there is a single point of
call for all classes in the hierarchy, where you can put your
pre- and post-conditions, and the virtual interface is never
exposed for an occasional non-virtual call (where you can't tell
just by looking which of the functions is going to be called).

V
 
W

werasm

werasm wrote:

But that's the whole point! The client of A or B or any other
derived classes will have to always call doFoo() instead of
'foo' (which can have different behaviour due to static or
dynamic binding, IOW when calling through a pointer or directly
for an object).


What template method? What are you talking about?

http://en.wikipedia.org/wiki/Template_method

More or less exactly what you are describing, but not
good enough. The Template method pattern (sorry, I
assumed you were familiar with the name) dictates
the order of behaviour (Pre-conditions, Post-conditions
etc.) iff there is an order (in the case of the example
there was none).

Client A wants to use Service A, and Client A dictates
what it requires in terms of service. In the case
of "Template Method" (what you have described) the
service exists prior to the client using it (mostly),
whereas often it works the other way around (in which
case the pre/post conditions live in the calling code
and not in the called code). The client often may
no too little of the implementation to dicate anything
apart from knowing what service it wants (which brings
you back to pure virtual).
Huh?


Does what, exactly?

This (or the example) allows the interface to be
used only by the client that required it and gives
the interface a single responsibility (in terms of
one client only). For me this solves the problem
of the OP better (in this particular case where
doFoo does not provide any additional encapsulation
as no pre/post conditions exist as the service
implementation is unknown, and if they did exist,
they could be performed in the calling code too).
The idiom of having all virtual interface private (pure or not)
and having a public interface that just forwards the calls to the
virtual function, is well known and commonly accepted in the
industry.

Yes, I realize this (Its called "template method", not?, but this
to me was not what the OP was looking for (perhaps he was, but
I saw something else in his request).
The main advantage is that there is a single point of
call for all classes in the hierarchy, where you can put your
pre- and post-conditions, and the virtual interface is never
exposed for an occasional non-virtual call (where you can't tell
just by looking which of the functions is going to be called).

I realize this. When I said "no point" I meant with respect to
the case he specified (where pre/post conditions don't
necessarily exist or are not known). Sometimes interfaces are merely
used (in OOP) to convey intent and specify/dictate responsibility.
In these cases the idiom you make mention of is lacking, whilst
my example makes this clear.
 
R

Ron Natalie

Firkraag said:
}

The above code works properly. As far as I know LSP is not being
violated here. So why FAQ lite says this:
http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.1

First the name is looked up.
Then overloads for the found name are considered.
Then the access for the best overload is considered.

THEN, if the function is virtual, the final overrider is
invoked.


As with most everything else in C++, the thing that matters
is the static declaration at the pointer of the call.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top