"All public methods should be virtual" - yes or no / pros & cons

  • Thread starter Thread starter Ken Brady
  • Start date Start date
[snip]
The general point Brandon is making is that in C++ you will easily
"use" a lot of nifty features that you do not understand without
being aware of it, it is unfornunately not a choice in many cases (no
pun intended). That has been acknowledged by the C# designers.

I don't see why it is C++ fault when ignorant programmers abuse features
they don't understand. Moreover, C# is getting more complex with the next
version and even today people criticize C# for being too complex:

http://groups.google.com/[email protected]#link1

[snip]
And oh (I almost forgot): "my language is better than yours".

Language wars are silly.

Regards,

Andreas
 
Bronek Kozicki said:
[still crossposting and asking for crosspost]

Nah, let them read it here.
Could you please provide example code in C#, with the same (or better ;) )
functionality as Carl's example ? It would be interesting to compare
idiom your are proposing with the one described by Carl.

Okay, we used a number of terms in the past posts ranging from polymophism
to events and delegates. What the purpose of Carl's example boils down to is
to that of a callback. In C# "delegates" are used to implement both events
and callbacks. One could argue that events basically are callbacks, let's
not get into that right now.

I say if you really want a callback, then don't use polymorphism because
using ploymorphism is a semantical statement (I like big words, please
forgive me). It implies that there is some basic entity that may have
different incarnations. In Carl's example this is not the case, there it is
plain vanilla straight ahead processing with the possibility for the client
to hook into the control flow (I am really enjoying this).

I read about delegates a couple of weeks ago in "Inside C# 2nd edition" and
grasped about half of it. The identifier names weren't all that well chosen
which made it hard to read and I thought I'd have a closer look the first
time I would need it. Which is now, you gave me the little push I needed.

I tried to stay close to the original idea of a series of tasks that should
be inaccessable from the client's side apart from one particular sub-task
that the client may replace. A string is build and the client gets the
oppurtunity to insert its piece in it. If it declines a defaul piece is used
instead.


=============== START OF CODE ===================

using System;
using System.Text;

class PlugHost
{
private string data;

public delegate void ExternalProcessorDelegate(ref string data);
public void DoProcessingAndDelegatePartOfTheWork(ExternalProcessorDelegate
custom)
{
data = "_StandardPartA_";

// (only) if the client provided its own logic,
// use that to obtain the middle part.
if (custom == null)
{
data += "_StandardPartB_";
}
else
{
string strCustom = null;
custom(ref strCustom);
data += strCustom;
}

// add another standard part
data += "_StandardPartC_";
}
public string GetResult() { return data; }
}

class App
{
static private void MyCustomProcessor(ref string s)
{
// add my two cents to the effort
s = "CustomPart";
}

static void Main()
{
PlugHost plugHost = new PlugHost();
// wrap reference to MyCustomProcessor in delegate
PlugHost.ExternalProcessorDelegate myProcessorDelegate = new
PlugHost.ExternalProcessorDelegate(MyCustomProcessor);

// call DoProcessingAndDelegatePartOfTheWork, passing the delegate
plugHost.DoProcessingAndDelegatePartOfTheWork(myProcessorDelegate);

Console.WriteLine("Result: {0}", plugHost.GetResult());
}
}

=============== END OF CODE ===================


Martin.
 
The general point Brandon is making is that in C++ you will easily
I don't see why it is C++ fault when ignorant programmers abuse features
they don't understand. Moreover, C# is getting more complex with the next
version and even today people criticize C# for being too complex:

No one is blaming anyone or anything. We just feel it is a good idea to
avoid language constructs that are easily misunderstood when designing a
language. We can now look back and make judgements about particular language
features, weighing the costs aganst the benefits. Some features may have
proven to be more of a burden than a bless. Of course it will always be the
programmer's fault but that naked assessment will not make up for the damage
done. It is a good thing that the industry is learning from its mistakes.
Making things "tricky" has proven to be too expensive just to say "fat luck,
you should have hired smarter programmers".

We have safety belts, we have fire alarms and we are going to program
managed code using new languages.

It is only natural to frown on a next generation of anything and since you
are dragging in hot shots to support your case, here's a quote from a
reasonable informed guy in the industry: "I don't need no stinkin' garbage
collector, I obviously know what I'm doing". And he too was turned around
pretty soon too :-).

Martin.


The quote is Jeffrey Richter's, sharing his early emotions on .NET with the
audience at the Developer Days 2002 in The Netherlands.
 
Martin said:
No one is blaming anyone or anything. We just feel it is a good idea
to avoid language constructs that are easily misunderstood when
designing a language. We can now look back and make judgements about
particular language features, weighing the costs aganst the benefits.
Some features may have proven to be more of a burden than a bless. Of
course it will always be the programmer's fault but that naked
assessment will not make up for the damage done. It is a good thing
that the industry is learning from its mistakes. Making things
"tricky" has proven to be too expensive just to say "fat luck, you
should have hired smarter programmers".

We have safety belts, we have fire alarms and we are going to program
managed code using new languages.

It is only natural to frown on a next generation of anything and
since you are dragging in hot shots to support your case, here's a
quote from a reasonable informed guy in the industry: "I don't need
no stinkin' garbage collector, I obviously know what I'm doing". And
he too was turned around pretty soon too :-).

I guess I have to clarify that I'm quite fond of C# and I use it for most of
my current programming work (I also still use C++). I only posted in this
thread because someone else made IMO unfounded statements about C++. The
archives'll tell you that I do the same when someone makes IMO unfounded
statements about C#.

Regards,

Andreas
 
Brian Ross said:
[...]
Is it not already possible for derived classes to invoke those functions
anyways. It is a function that belongs to derived afterall? For example:

class Base
{
private:
virtual void f() = 0;
};

class Derived : public Base
{
public:
void g()
{ f(); }

private: // or whatever

virtual void f()
{}

};

I don't see how whether the base class having f() as private or protected
makes any difference to Derived? Additionally, whether 'f()' is protected or
private will make no difference to non-derived classes - it will be
inaccessable.

The difference is, that when you make
'f()' private in the base class, authors
of derived classes won't have to wonder
whether to call 'f()' or not.
Whether 'f()' is protected or private _will_ make a difference if any
derived class tries to call 'f()' statically ( ie.. Base::f() ). If it was
private, it would fail.

Right. Usually that's a good thing.

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Martin Maat said:
Okay, that makes sense. Technically that is.


Name one :-).

The Template Method pattern:

class Interface {
public:
void algorithm()
{
some_stuff();
customizable_stuff();
some_more_stuff();
more_customizable_stuff();
}
private:
void some_stuff() {}
void some_more_stuff() {}
virtual void customizable_stuff() = 0;
virtual void more_customizable_stuff() {}
};

The 'algorithm()' member function implements
some algorithm. Derived classes can plug into
this and customize some of it.
There is no need for derived classes to call
'customizable_stuff()' etc. directly, and thus
they shouldn't be allowed. However, they can
(in the pure virtual case: must) replace them.
Just by looking at the class declaration, this
is clear right away to anyone knowing the
pattern.
I am serious, I have been giving this some thought just now but I cannot
come up with a sensible reason to do this. It makes me think of Tommy Cooper
("Pick a card, any card... No, not that one!"). Here's a method, you can use
it.. No you can't!.

"Here is a method, that you can override.
No, you shouldn't call it. Oh right, it
says so on the lable already."


Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Martin Maat said:
[...] However, Jim
Hyslop and Herb Sutter wrote a nice article that provided a reasonable
example: http://www.cuj.com/documents/s=8000/cujcexp1812hyslop/

Hehe, this article says it all. One C++ knoledgable person that loves the
fact that her code is smart and hard to grasp. The next developer needs to
go back to the first one before he can use the class properly.

Only if they don't know the patterns used.
With the very same argument, you can reject
the usage of increment/decrement operators,
since they just hide what's really done
from people who don't know them.
That is the
whole point, it isn't wrong but it works in a counter-intuitive way,
defeating the purpose of having these visibility layers.

In the years I am using C/C++, I have come
across many, many things which I considered
very, very couter-intuitive. Yet they are
done regularly and understood by most (for
some definition of "most") programmers (for
some definition of "programmers"). For many
of those, it was just that I hadn't learned
enough yet. (For some I'd still argue.)
It was the very same with private virtuals.
If you use google, you might find that at
one (not so long ago) time I was stunned to
learn that this even is possible. Now I use
it regularly. At first, my co-workers were
stunned when they saw it. Any guess what:
Meanwhile, they start to use it, too.
[...]
Martin.

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Martin Maat said:
You seem to suggest that C++s support for private virtual functions makes
C++ less robust. Funny, I'd claim exactly the opposite.
"Industrial robustness" in the real world means simplifying things so
that the vast majority of average programmers understand what's going
on.
[...]
I really fail to see how private virtuals hurt "industrial robustness".

The general point Brandon is making is that in C++ you will easily "use" a
lot of nifty features that you do not understand without being aware of it,
[...]

With the same reasoning you could reject
any tool more complicated than a hammer
and a bag of nails, since you can misuse
them in so many hurtful ways. Yet, in the
hand of a skilled craftsman, they can be
very powerful tools that allow him to do
things that are less elegant or very hard
to do without them.
In the private virtual discussion, robustness may not be an issue. I would
call it "not elegant".

I find Template Method a _very_ elegant
pattern. Once you explain it to someone,
they know exactly what it is about, how
to use it, and how to recognize it. And
using private virtuals, it does exactly
what the class declaration indicates it
is doing.
Every time something new comes along the established lot will say "Kid's
stuff, no gain, too slow, don't need it". And after a while we all learn to
appreciate it.

Yeah. Seems like private virtual are new
to many here.
And oh (I almost forgot): "my language is better than yours".

According to which definition of "good"? :)

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Martin Maat said:
[...]
No one is blaming anyone or anything. We just feel it is a good idea to
avoid language constructs that are easily misunderstood when designing a
language. [...]

Define "easily". Oh, and for whom?
I have been teaching programming and
IMO every language is too hard to
grasp for someone, and easily used
by others.
[...]
Martin.
[...]


Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Martin Maat said:
[...]
Well, they didn't have these fancy editors back then that enabled the
programmer to collaps the implemantation did they? So seperating interface
and implementation was just practical [...]

If I hadn't been taught it before, the
first time I came across an app that
had a couple 100kLOC, with many people
designing and implementing parts of it
simultaniously, I know what separation
of interface and implementation is good
for. IMHO that's why so many languages
have it.

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
2004? When was C++ spec last updated ?
2003.

[...]

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
And what language do you think will be used most in the CLI world and where
the jobs are :D C#

Ugh, that again.
For a sterter, what language do you think the
foundations of the CLI are written in?

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Uhh what language do you think I code with C# ?


Hendrik Schober said:
And what language do you think will be used most in the CLI world and where
the jobs are :D C#

Ugh, that again.
For a sterter, what language do you think the
foundations of the CLI are written in?

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Martin said:
Bronek Kozicki said:
[still crossposting and asking for crosspost]

Nah, let them read it here.
Could you please provide example code in C#, with the same (or
better ;) ) functionality as Carl's example ? It would be
interesting to compare idiom your are proposing with the one
described by Carl.

Okay, we used a number of terms in the past posts ranging from
polymophism to events and delegates. What the purpose of Carl's
example boils down to is to that of a callback.

This is not true. The flow-control pattern that exists in my example is
similar to that of a callback. The purpose, however, is completely
different. The example you provided is one of a "client" hooking into the
behavior of an object. While a derived class is a "client" of it's base, it
is a very special kind of client. The idiom that I illustrated shows how a
base class (think interface, but in abstract terms, not C# terms) can be
constructed to ensure that ALL classes that implement that interface satisfy
the invariants (guarantees) that the interface designer intended. Delegates
cannot provide this guarantee, since there's no way to REQUIRE that a
derived class (or client) actually provide a delegate to one of it's methods
and hook it into the correct event of the base class.
In C# "delegates" are
used to implement both events and callbacks. One could argue that
events basically are callbacks, let's not get into that right now.

Delegates (and events) in C# are precisely (and solely) a systematic
mechanism for creating and invoking callbacks. Nothing more. That's not to
say they're not useful - they are quite useful. But they're not a mechanism
one would use to implement design by contract.

-cd
 
Well, they didn't have these fancy editors back then that enabled the
programmer to collaps the implemantation did they? So seperating interface
and implementation was just practical [...]

If I hadn't been taught it before, the first time I came across an app that
had a couple 100kLOC, with many people designing and implementing
parts of it simultaniously, I know what separation of interface and
implementation is good for. IMHO that's why so many languages
have it.

No, don't mean a thing. What you are refering to is having separate
(implementation) code files for different parts of a system. That is a good
thing. Implementation and interface though, beit for a single class or for
any code library, are logically one and the same. Seperating them physically
does not bring any gain as far as maintainability is concerned. You will
always lock both when working on either. Working on just the interface
without touching the implementation is pointless and if you are changing the
implementation you don't want anyone else to mess with the interface. If
anyone did want to mess with the interface he would most certainly want to
mess with the implementation as well.

Mind that the idea of .c and .h files stems from the pre-OO days. People
mainly extended that to .cpp and .h because they were used to working that
way. For class definitions there is no point in doing so as far as
maintainability is concerned..

With C# it is not only because there is no longer a practical need for
separation that everything is in one file now, it is a design decision based
on the notion that a class definition is a self-contained unit. Think of the
problems that would arise for the namespace system if it were different.

Martin.
 
No one is blaming anyone or anything. We just feel it is a good idea to
avoid language constructs that are easily misunderstood when designing a
language. [...]

Define "easily". Oh, and for whom? I have been teaching programming and
IMO every language is too hard to grasp for someone, and easily used
by others.

As a teacher of programming languages you will acknowledge that in C it is
relatively easy to write code that does something different than what you
intended yet compiles without any problem. In Pascal it is a lot harder to
do that, even in Basic it is a lot harder to do that. Just a few examples:

if (a = 3) { /* do whatever */ } // I mean "if a equals 3, do whatever"
but instead a is assigned the value 3 which evaluates to true.
MyFunction; // I want to execute the function but by forgetting the
parenthesis a function pointer is evaluated and dropped into a void instead.
Case fall-though is another nice one.

It is for good reason that in championships for writting the most obfuscated
one-liner the C language rules :-).

Martin.
 
Martin Maat said:
Well, they didn't have these fancy editors back then that enabled the
programmer to collaps the implemantation did they? So seperating interface
and implementation was just practical [...]

If I hadn't been taught it before, the first time I came across an app that
had a couple 100kLOC, with many people designing and implementing
parts of it simultaniously, I know what separation of interface and
implementation is good for. IMHO that's why so many languages
have it.

No, don't mean a thing. What you are refering to is having separate
(implementation) code files for different parts of a system.

No. What I am talking about it having the
interface separated from its implementation.
That is a good
thing. Implementation and interface though, beit for a single class or for
any code library, are logically one and the same. Seperating them physically
does not bring any gain as far as maintainability is concerned.

It does. It makes clients independand of
the implementation. They only depend on
the interface.
You will
always lock both when working on either.

I rarely ever change a header.
(I'd be dead within a week if I did change
interfaces half as often as I change their
implementations. People would be queueíng
at the table tennis room while their
machines are locked with the compiler
running and had a _lot_ of time to consider
what bad things to do to me. :o> )
Working on just the interface
without touching the implementation is pointless and if you are changing the
implementation you don't want anyone else to mess with the interface. If
anyone did want to mess with the interface he would most certainly want to
mess with the implementation as well.

Althoguh I think it is possible, it is
uncommon to lock files using CVS. I have
never done it and haven't heard from anyone
doing it here.
Mind that the idea of .c and .h files stems from the pre-OO days. People
mainly extended that to .cpp and .h because they were used to working that
way. For class definitions there is no point in doing so as far as
maintainability is concerned..

You're kidding, aren't you?
Suppose I have a class 'X', used in just
about every part of a big project. Now I
found a bug in 'X::f()' and need to fix
that. Why would I need to lock/change the
interface? 'X' was designed a long time
ago, its interface had a lot of time to
mature -- it is stable. I I change its
interface just so, everyone on the project
will be prevented from working for an hour
or two, just so. I wouldn't dream of doing
this without discussing the changes first.
All I need to do is fix 'X::f()'.
With C# it is not only because there is no longer a practical need for
separation that everything is in one file now, it is a design decision based
on the notion that a class definition is a self-contained unit. Think of the
problems that would arise for the namespace system if it were different.

I don't know C# and I don't know its
namespace system. What I know is this: If
I cause everybody here to recompile just
because I fix some implementation, I'd
be in serious trouble.


Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Martin Maat said:
No one is blaming anyone or anything. We just feel it is a good idea to
avoid language constructs that are easily misunderstood when designing a
language. [...]

Define "easily". Oh, and for whom? I have been teaching programming and
IMO every language is too hard to grasp for someone, and easily used
by others.

As a teacher of programming languages you will acknowledge that in C it is
relatively easy to write code that does something different than what you
intended yet compiles without any problem. In Pascal it is a lot harder to
do that, even in Basic it is a lot harder to do that. [...]

Right. But first, we are talking C++ here
(stronger type checking, 'const', less use
for the preprocessor etc.). Second, while
it might still be relatively easy to write
some obfuscated code in C++, we were not
talking about "some code" here, but about
private virtual functions and whether they
are a language construct that is easily
misunderstood.
(Incidently, I taught the use of such just
last week. Nobody found them specifically
strange, nobody asked any question about
them, and in their homework they'll just
use it.)
[...]
It is for good reason that in championships for writting the most obfuscated
one-liner the C language rules :-).

I think with the operator overloading,
implicit conversions and all that stuff,
C++ would be a lot easier to write
obfuscated code if you wanted to.

Schobi

--
(e-mail address removed) is never read
I'm Schobi at suespammers dot org

"Sometimes compilers are so much more reasonable than people."
Scott Meyers
 
Martin Maat said:
No one is blaming anyone or anything. We just feel it is a good idea to
avoid language constructs that are easily misunderstood when designing a
language. [...]

Define "easily". Oh, and for whom? I have been teaching programming and
IMO every language is too hard to grasp for someone, and easily used
by others.

As a teacher of programming languages you will acknowledge that in C it is
relatively easy to write code that does something different than what you
intended yet compiles without any problem. In Pascal it is a lot harder to
do that, even in Basic it is a lot harder to do that. Just a few examples:

if (a = 3) { /* do whatever */ } // I mean "if a equals 3, do whatever"
but instead a is assigned the value 3 which evaluates to true.
MyFunction; // I want to execute the function but by forgetting the
parenthesis a function pointer is evaluated and dropped into a void instead.
Case fall-though is another nice one.

A quality compiler will probably produce a warning that the expression
always evaluates to true (if a is a numeric type). Hopefully ending the
warning with 'Is this what you intended?' ;-). But as the quality of the
compilers increase, so should the quality of warnings. And since warnings
can be errors that is a good thing.

But:
int g(int x);
// ...
int x=3, a;
if( a = g(x) ) { ... }

Would quality compilers warn about that? Suppose you intended it?

Tom.
 
Martin Maat said:
Well, they didn't have these fancy editors back then that enabled the
programmer to collaps the implemantation did they? So seperating interface
and implementation was just practical [...]

If I hadn't been taught it before, the first time I came across an app that
had a couple 100kLOC, with many people designing and implementing
parts of it simultaniously, I know what separation of interface and
implementation is good for. IMHO that's why so many languages
have it.

No, don't mean a thing. What you are refering to is having separate
(implementation) code files for different parts of a system. That is a good
thing. Implementation and interface though, beit for a single class or for
any code library, are logically one and the same. Seperating them physically
does not bring any gain as far as maintainability is concerned. You will
always lock both when working on either. Working on just the interface
without touching the implementation is pointless and if you are changing the
implementation you don't want anyone else to mess with the interface. If
anyone did want to mess with the interface he would most certainly want to
mess with the implementation as well. [...]
Martin.

I like a seperate file with an overview of what your interface for a class
is (speaking about implementation of classes). Now that I code JAVA
sometimes, I really miss the headers, and the feeling doesn't go away.

The problem is that such an overview cannot be generated from just a source
file, because it isn't stable and could contain bugs that do not allow it to
be generated. A side bar with an alfabetically sorted list with members and
stuff is to my feeling not enough. A header file that groups functionality
and members etc gives me a nicer overview.

And as for the locking, a lot of times I (or my colleagues) lock the
implementation file during debug phases.

Tom.
 
Back
Top