Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Re: only call dtor of derived class?

Reply
Thread Tools

Re: only call dtor of derived class?

 
 
Jens Thoms Toerring
Guest
Posts: n/a
 
      12-03-2011
Heck <(E-Mail Removed)> wrote:
> Can it be (easily) arranged to call only the destructor of a derived
> class, and call none of the destructors in the classes from which it
> derives? This may seem to seek to negate the simple and compelling
> value of the destructor tree, so, perhaps my design is flawed, but,
> perhaps not.


> I wrote the classes I'm wrestling with to handle memory-mapped files.
> The files that already exist require different sharing and access
> rights and different attributes than the files that are to be created
> and the files that are to be created are to be resized upon completion
> (extra bytes removed).


> Rather than pass in a small slew of args to effect this, I just wrote
> the sequence of calls twice, each set appropriately parameterized.
> With respect to the constructors, the derived classes happen to
> require parameters the base class does not, so, with that I can call
> the constructors appropriately, but as far as the destructors, it's
> either not allowed or it is and I don't know how to control which
> class gets which series of destructors called for it.


> I've pointers in the base class that I want to use in the derived
> classes.


> Simple tests in the base class' destructor will suffice to restrict
> the execution to the approriate objects, but, is there a way C++'s
> semantics of inheritance facilitates such a scenario?


As Victor has put it short and bluntly you're on the wrong
track. Not only can't that be done but you're violating one
of the most basic principles of OOP, i.e. that inheritance
is all about an "is-a" relationship. If for the derived class
the destructor of the base class can't be called for what-
ever reasons then the derived class isn't an object of the
type that is a specialization of the type of object the base
class is for. Even when you have to check in the base class
what actual kind of oject it's dealing with (as you seem to
consider as a "fix") you're on the wrong path.

A base class must never, ever have any idea what it's derived
classes do look like whatsoever. If it does it's already too
specialized for being a base class you can derive all the
types you want to from.

What you seen to be having here is a case like a base class
for vehicles where you started your design with the assump-
tion that all vehicles have an engine that can be switched
on and off. Thus you may have something to switch off thed
engine in the destructor of the "Vehicle" class. And that
works quite nicely for cars, motorcycles, lorries, most
airplanes etc. But then you realize that you also want to
have bicycles and gliders - and suddenly everything seems
to break down. You now ask if there's a way to avoid having
the destructor getting called that switches off the (non-
existent) engine. But the problem is that your base class
is simply too specialized.

Instead of "repairing" things in the base class by testing
if the vehicle has an engine and make it behave accordingly
(thus forcing the base class to be "aware" of what additional
properties its descendants can have) create a new, more
general class that doesn't know about the existence of an
engine and create two intermediate base classes, both in-
heriting from the original base class, that are for vehicles
with and without an engine. Now make your already existing
derived classes derive from the new intermediate class for
"vehicle with an engine" while deriving bicycles and gliders
from the new internediate class for "vehicle without an
engine".

The way you describe your problem it looks as if you started
with the assumption that memory-mapped files are all the same,
so having a single base class for them looked reasonable. But
now you've recognized that the property of just being "memory-
mapped" isn't what they all have in common. So revise your base
class to contain only what's common to all of them and create
intermediate classes you can derive from the more specialized
cases where there aren't any issues when all the destructors
are called.
Regards, Jens
--
\ Jens Thoms Toerring ___ http://www.velocityreviews.com/forums/(E-Mail Removed)
\__________________________ http://toerring.de
 
Reply With Quote
 
 
 
 
Pavel
Guest
Posts: n/a
 
      12-05-2011
Heck wrote:
> On Sat, 03 Dec 2011 22:07:56 -0500, Heck
> <(E-Mail Removed)> wrote:
>
>> On 3 Dec 2011 00:32:35 GMT, (E-Mail Removed) (Jens Thoms Toerring)
>> wrote:
>>
>>> Heck<(E-Mail Removed)> wrote:
>>>> Can it be ...my design is flawed...

>
>>> As Victor has put it short and bluntly you're on the wrong
>>> track. Not only can't that be done but you're violating one
>>> of the most basic principles of OOP, i.e. that inheritance
>>> is all about an "is-a" relationship. If for the derived class
>>> the destructor of the base class can't be called for what-
>>> ever reasons then the derived class isn't an object of the
>>> type that is a specialization of the type of object the base
>>> class is for. Even when you have to check in the base class
>>> what actual kind of oject it's dealing with (as you seem to
>>> consider as a "fix") you're on the wrong path.
>>>
>>> A base class must never, ever have any idea what it's derived
>>> classes do look like whatsoever. If it does it's already too
>>> specialized for being a base class you can derive all the
>>> types you want to from.
>>>
>>> What you seen to be having here is a case like a base class
>>> for vehicles where you started your design with the assump-
>>> tion that all vehicles have an engine that can be switched
>>> on and off. Thus you may have something to switch off thed
>>> engine in the destructor of the "Vehicle" class. And that
>>> works quite nicely for cars, motorcycles, lorries, most
>>> airplanes etc. But then you realize that you also want to
>>> have bicycles and gliders - and suddenly everything seems
>>> to break down. You now ask if there's a way to avoid having
>>> the destructor getting called that switches off the (non-
>>> existent) engine. But the problem is that your base class
>>> is simply too specialized.
>>>
>>> Instead of "repairing" things in the base class by testing
>>> if the vehicle has an engine and make it behave accordingly
>>> (thus forcing the base class to be "aware" of what additional
>>> properties its descendants can have) create a new, more
>>> general class that doesn't know about the existence of an
>>> engine and create two intermediate base classes, both in-
>>> heriting from the original base class, that are for vehicles
>>> with and without an engine. Now make your already existing
>>> derived classes derive from the new intermediate class for
>>> "vehicle with an engine" while deriving bicycles and gliders
>> >from the new internediate class for "vehicle without an
>>> engine".
>>>
>>> The way you describe your problem it looks as if you started
>>> with the assumption that memory-mapped files are all the same,
>>> so having a single base class for them looked reasonable. But
>>> now you've recognized that the property of just being "memory-
>>> mapped" isn't what they all have in common. So revise your base
>>> class to contain only what's common to all of them and create
>>> intermediate classes you can derive from the more specialized
>>> cases where there aren't any issues when all the destructors
>>> are called.
>>> Regards, Jens

>>
>> Yes, I understand. I agree my conception was flawed. In C, you can
>> start with a flawed design such as this and make it work despite its
>> deficiencies. This illustrates that C++ is both more powerful and
>> more precise in its semantics than C. Thanks for your detailed and
>> helpful reply.

>
> I don't mean to say that you can't simply make something flawed work
> in C++, as you can in C. C++ is just as flexible as C. The advantage
> of C++ over C in this regard is that it pragmatically encourages a
> coder to design and write clearly, and to some degree even promotes
> clear thinking.

Generalizations you said and heard are both right and wrong. OOP is a mixed
blessing. C++ (and OOP) make you "design for future" and no one knows the
future. Changes in the requirements often alter what are "the right things" to
hide and expose; and then the cognitive biases and non-technical issues stand in
a way of re-designing the class structure all over again as it would be
appropriate "in an ideal world". The result is often much messier than in
procedural-oriented language. A case in point is Linux kernel: it is not only
because of its low-level nature is it beneficial that it is written in C rather
than C++: it is also because the kernel is extremely dynamic, the whole
subsystems are being fully re-written between minor versions *and their roles
and responsibilities (separation of duties) are changing at that*. OOP works
well when roles and responsibilities of base classes are relatively stable; this
is far from true in many subject areas.

Sometimes even in well-defined areas programming with classes is more limiting
and creates less usable legacy than programming with subroutines/functions only.
A good example are matrix manipulation libraries. If you choose to manipulate
with matrices in an old-fashion FORTRAN way (i.e. based on conventions about
matrix internal representation and adding functions perusing it which is a
mortal sin in OOP), your can reuse the libraries written as far back as in 70th
(NAG, IMSL, SSP etc) from your C++ or C program and all these are
data-compatible each with other. What I mean by that, you can allocate and
initialize internal representations (arrays) in C++, call some subroutines from
NAG, call SSP subroutines from SSP on the results, then call your own C function
you wrote decades later etc. all without any extra copying or conversions.

On the other hand, I have yet to see a pair of C++ matrix manipulation libraries
that could re-use one another's matrices without copying/conversions.

Back to your original problem, I am not sure what the responsibilities of the
base class were and why the destructor of the base class should have done
anything non-common (I would only do unmapping there based on the information
you gave; closing could be done right after mapping in constructors if you write
for a POSIX-compliant system; resizing etc. could be done in derived classes
only). If you showed your tentative hierarchy structure, it would be easier to help.

Just a thought,
-Pavel
 
Reply With Quote
 
 
 
 
Stuart Redmann
Guest
Posts: n/a
 
      12-05-2011
Sorry for interrupting your discussion

On 5 Dez., Pavel wrote:
> [snip].
> OOP is a mixed blessing. C++ (and OOP) make you "design for future"
> and no one knows the future. Changes in the requirements often alter
> what are "the right things" to hide and expose; and then the cognitive
> biases and non-technical issues stand in a way of re-designing the class
> structure all over again as it would be appropriate "in an ideal world".
> The result is often much messier than in procedural-oriented language.



I found a nice page that might be of interest to you:
http://reocities.com/tablizer/oopbad.htm
There is also a very sweet observation about communism in it.


> A case in point is Linux kernel: it is not only
> because of its low-level nature is it beneficial that it is written in C rather
> than C++: it is also because the kernel is extremely dynamic, the whole
> subsystems are being fully re-written between minor versions *and their
> roles and responsibilities (separation of duties) are changing at that*.
> OOP works well when roles and responsibilities of base classes are
> relatively stable; this is far from true in many subject areas.



I thought that Linus didn't want to use C++ because C++ uses much more
stack memory (which is quite limited in the kernel). Perhaps I should
do some research...

However, even if we only used procedural programming under Linux, it
would still be quite nice to use objects in the functions. Whole
chapters in Linux tutorials are dedicated to the containers (mostly
lists) inside the kernel. If Linux were written under C++, we would
never have to read those chapters.

I agree that many problem-spaces cannot be adequately structured by
OOD, but that should not mean that we should design each and every sub-
problem-space in an procedural way. The beauty of C++ is that we can
use both design paradigms at the same time. Besides, C++ offers more
than just OOD (think of generics).


> Sometimes even in well-defined areas programming with classes is more
> limiting and creates less usable legacy than programming with
> subroutines/functions only. A good example are matrix manipulation
> libraries. If you choose to manipulate with matrices in an old-fashion
> FORTRAN way (i.e. based on conventions about matrix internal
> representation and adding functions perusing it which is a mortal sin
> in OOP), your can reuse the libraries written as far back as in 70th
> (NAG, IMSL, SSP etc) from your C++ or C program and all these are
> data-compatible each with other. What I mean by that, you can allocate
> and initialize internal representations (arrays) in C++, call some
> subroutines from NAG, call SSP subroutines from SSP on the results,
> then call your own C function you wrote decades later etc. all
> without any extra copying or conversions.
>
> On the other hand, I have yet to see a pair of C++ matrix manipulation
> libraries that could re-use one another's matrices without
> copying/conversions.

[snip]

My co-worker spent quite some time to write a C++ wrapper for the BLAS
library so that he can use operator+ and operator*. Such simple things
make the code much more readable. Why can't we do that under C?

Regards,
Stuart
 
Reply With Quote
 
Werner
Guest
Posts: n/a
 
      12-05-2011
On Dec 5, 9:36*am, Stuart Redmann <(E-Mail Removed)> wrote:
> Sorry for interrupting your discussion
>
> On 5 Dez., Pavel wrote:
>
> > [snip].
> > OOP is a mixed blessing. C++ (and OOP) make you "design for future"
> > and no one knows the future. Changes in the requirements often alter
> > what are "the right things" to hide and expose; and then the cognitive
> > biases and non-technical issues stand in a way of re-designing the class
> > structure all over again as it would be appropriate "in an ideal world"..
> > The result is often much messier than in procedural-oriented language.

>
> I found a nice page that might be of interest to you:http://reocities.com/tablizer/oopbad.htm
> There is also a very sweet observation about communism in it.


I did not read all the oopbad stuff, but I've seen a lot
of bad code due to programming in OOP languages without OOD.

I've also found that using good OOD in conjunction templates
allows one to reuse and share code. Especially SRP (Single
Responsibility Principle).

I've also noticed that many a procedural programming
uses some form of pseudo OOD to organize his code, in
fact - almost all of the ones I've seen do this (Even
if this means using some directory structure or file
for related functions).

OOD gives us the means to factorize the
organization/architecture into byte sizes, heh?

OT:

About communism (and maybe this is your point too):

Perfect communism would make perfect sense. As reference
you can consider the early church in Acts (The Bible).
Perfect OOP perhaps too? Yes, early 20th century to
modern day communism have capitalist leaders, hence it
does not make sense . They are into "self-enrichment",
and living at the cost of others. Perhaps many OOD
programs have C programmers running the design (with
respect)?

Capitalism...

- Will evolve something terrible yet, the consequences
far worse (global, perhaps catastrophical).

Regards,

Werner


 
Reply With Quote
 
Stuart Redmann
Guest
Posts: n/a
 
      12-05-2011
On Dec 5, Stuart Redmann wrote:
> > I found a nice page that might be of interest to
> > you:http://reocities.com/tablizer/oopbad.htm
> > There is also a very sweet observation about communism in it.


On 5 Dez., Werner wrote:
> I did not read all the oopbad stuff, but I've seen a lot
> of bad code due to programming in OOP languages without OOD.


This is very likely the reason why many don't like OOP: If you use it
badly, you'll screw it up much more magnificently

Seriously, I think that the author of http://reocities.com/tablizer/oopbad.htm
wants to say that there are some problems that can never, ever be
accurately represented in an OO manner, no matter how much effort you
spend in OOD. I would agree, since I recently encountered some data-
driven problem where OO does not make much sense.

[snip]

On 5 Dez., Werner wrote:
> OT:
>
> About communism (and maybe this is your point too):
>
> Perfect communism would make perfect sense. As reference
> you can consider the early church in Acts (The Bible).
> Perfect OOP perhaps too? Yes, early 20th century to
> modern day communism have capitalist leaders, hence it
> does not make sense . They are into "self-enrichment",
> and living at the cost of others. Perhaps many OOD
> programs have C programmers running the design (with
> respect)?


In fact, I didn't have this in mind, I just found the remark about
communism quite concise.

Regards,
Stuart
 
Reply With Quote
 
Juha Nieminen
Guest
Posts: n/a
 
      12-05-2011
Pavel <(E-Mail Removed)> wrote:
> OOP works
> well when roles and responsibilities of base classes are relatively stable; this
> is far from true in many subject areas.


Inheritance is not the biggest advantage of OOP (even though it was intended
to be when originally designed). It's still extremely useful, of course
(for example inheritance-heavy OOP fits GUI implementations so well that
one could well think that OOP was invented precisely to solve the problem
of how to program GUIs). However, in average inheritance is not the strongest
point.

The biggest advantage of OOP is modularity. This goes well beyond what
you can do with C compilation units and structs. (Compilation units have the
big disadvantage that you can't "instantiate" more than one of them at a
time. C structs have accessibility and lifetime management problems.)

In C++ in particular OOP is a very strong tool because you can achieve a
high level of abstraction yet still produce very efficient code (especially
in term of memory usage, unlike most other OOP languages). RAII is also an
enormous advantage over C (even though strictly speaking it's not an OOP
feature).

Inheritance is just a "nice to have" extra feature that is sometimes
useful. It's not the main selling point.
 
Reply With Quote
 
Goran
Guest
Posts: n/a
 
      12-05-2011
On Dec 5, 4:07*am, Pavel
<(E-Mail Removed)> wrote:
> Heck wrote:
> > On Sat, 03 Dec 2011 22:07:56 -0500, Heck
> > <(E-Mail Removed)> *wrote:

>
> >> On 3 Dec 2011 00:32:35 GMT, (E-Mail Removed) (Jens Thoms Toerring)
> >> wrote:

>
> >>> Heck<(E-Mail Removed)> *wrote:
> >>>> Can it be ...my design is flawed...

>
> >>> As Victor has put it short and bluntly you're on the wrong
> >>> track. Not only can't that be done but you're violating one
> >>> of the most basic principles of OOP, i.e. that inheritance
> >>> is all about an "is-a" relationship. If for the derived class
> >>> the destructor of the base class can't be called for what-
> >>> ever reasons then the derived class isn't an object of the
> >>> type that is a specialization of the type of object the base
> >>> class is for. Even when you have to check in the base class
> >>> what actual kind of oject it's dealing with (as you seem to
> >>> consider as a "fix") you're on the wrong path.

>
> >>> A base class must never, ever have any idea what it's derived
> >>> classes do look like whatsoever. If it does it's already too
> >>> specialized for being a base class you can derive all the
> >>> types you want to from.

>
> >>> What you seen to be having here is a case like a base class
> >>> for vehicles where you started your design with the assump-
> >>> tion that all vehicles have an engine that can be switched
> >>> on and off. Thus you may have something to switch off thed
> >>> engine in the destructor of the "Vehicle" class. And that
> >>> works quite nicely for cars, motorcycles, lorries, most
> >>> airplanes etc. But then you realize that you also want to
> >>> have bicycles and gliders - and suddenly everything seems
> >>> to break down. You now ask if there's a way to avoid having
> >>> the destructor getting called that switches off the (non-
> >>> existent) engine. But the problem is that your base class
> >>> is simply too specialized.

>
> >>> Instead of "repairing" things in the base class by testing
> >>> if the vehicle has an engine and make it behave accordingly
> >>> (thus forcing the base class to be "aware" of what additional
> >>> properties its descendants can have) create a new, more
> >>> general class that doesn't know about the existence of an
> >>> engine and create two intermediate base classes, both in-
> >>> heriting from the original base class, that are for vehicles
> >>> with and without an engine. Now make your already existing
> >>> derived classes derive from the new intermediate class for
> >>> "vehicle with an engine" while deriving bicycles and gliders
> >> >from the new internediate class for "vehicle without an
> >>> engine".

>
> >>> The way you describe your problem it looks as if you started
> >>> with the assumption that memory-mapped files are all the same,
> >>> so having a single base class for them looked reasonable. But
> >>> now you've recognized that the property of just being "memory-
> >>> mapped" isn't what they all have in common. So revise your base
> >>> class to contain only what's common to all of them and create
> >>> intermediate classes you can derive from the more specialized
> >>> cases where there aren't any issues when all the destructors
> >>> are called.
> >>> * * * * * * * * * * * * * * *Regards, Jens

>
> >> Yes, I understand. *I agree my conception was flawed. *In C, you can
> >> start with a flawed design such as this and make it work despite its
> >> deficiencies. *This illustrates that C++ is both more powerful and
> >> more precise in its semantics than C. *Thanks for your detailed and
> >> helpful reply.

>
> > I don't mean to say that you can't simply make something flawed work
> > in C++, as you can in C. *C++ is just as flexible as C. *The advantage
> > of C++ over C in this regard is that it pragmatically encourages a
> > coder to design and write clearly, and to some degree even promotes
> > clear thinking.

>
> Generalizations you said and heard are both right and wrong. OOP is a mixed
> blessing. C++ (and OOP) make you "design for future" and no one knows the
> future. Changes in the requirements often alter what are "the right things" to
> hide and expose; and then the cognitive biases and non-technical issues stand in
> a way of re-designing the class structure all over again as it would be
> appropriate "in an ideal world". The result is often much messier than in
> procedural-oriented language. A case in point is Linux kernel: it is not only
> because of its low-level nature is it beneficial that it is written in C rather
> than C++: it is also because the kernel is extremely dynamic, the whole
> subsystems are being fully re-written between minor versions *and their roles
> and responsibilities (separation of duties) are changing at that*. OOP works
> well when roles and responsibilities of base classes are relatively stable; this
> is far from true in many subject areas.


This cannot go unchallenged .

What are saying here is that OOP is intrinsically more rigid than some
other style, and therefore unsuited for something like an OS kernel,
which is somehow more dynamic than something else. You are further
making an argument that for such software all should be left out in
the open, and that this will be more flexible. Well, yes, but the
price of doing that is that it all turns into a thing commonly known
as a "big ball of mud". That's it, really.

There's another thing: you are mostly talking about information
hiding, really. Well, if you fear rigidity, nothing stops you to leave
all out in the open and be done with it.

Goran.
 
Reply With Quote
 
Pavel
Guest
Posts: n/a
 
      12-11-2011
Goran wrote:
> On Dec 5, 4:07 am, Pavel
> <(E-Mail Removed)> wrote:
>> Heck wrote:
>>> On Sat, 03 Dec 2011 22:07:56 -0500, Heck
>>> <(E-Mail Removed)> wrote:

>>
>>>> On 3 Dec 2011 00:32:35 GMT, (E-Mail Removed) (Jens Thoms Toerring)
>>>> wrote:

>>
>>>>> Heck<(E-Mail Removed)> wrote:
>>>>>> Can it be ...my design is flawed...

>>
>>>>> As Victor has put it short and bluntly you're on the wrong
>>>>> track. Not only can't that be done but you're violating one
>>>>> of the most basic principles of OOP, i.e. that inheritance
>>>>> is all about an "is-a" relationship. If for the derived class
>>>>> the destructor of the base class can't be called for what-
>>>>> ever reasons then the derived class isn't an object of the
>>>>> type that is a specialization of the type of object the base
>>>>> class is for. Even when you have to check in the base class
>>>>> what actual kind of oject it's dealing with (as you seem to
>>>>> consider as a "fix") you're on the wrong path.

>>
>>>>> A base class must never, ever have any idea what it's derived
>>>>> classes do look like whatsoever. If it does it's already too
>>>>> specialized for being a base class you can derive all the
>>>>> types you want to from.

>>
>>>>> What you seen to be having here is a case like a base class
>>>>> for vehicles where you started your design with the assump-
>>>>> tion that all vehicles have an engine that can be switched
>>>>> on and off. Thus you may have something to switch off thed
>>>>> engine in the destructor of the "Vehicle" class. And that
>>>>> works quite nicely for cars, motorcycles, lorries, most
>>>>> airplanes etc. But then you realize that you also want to
>>>>> have bicycles and gliders - and suddenly everything seems
>>>>> to break down. You now ask if there's a way to avoid having
>>>>> the destructor getting called that switches off the (non-
>>>>> existent) engine. But the problem is that your base class
>>>>> is simply too specialized.

>>
>>>>> Instead of "repairing" things in the base class by testing
>>>>> if the vehicle has an engine and make it behave accordingly
>>>>> (thus forcing the base class to be "aware" of what additional
>>>>> properties its descendants can have) create a new, more
>>>>> general class that doesn't know about the existence of an
>>>>> engine and create two intermediate base classes, both in-
>>>>> heriting from the original base class, that are for vehicles
>>>>> with and without an engine. Now make your already existing
>>>>> derived classes derive from the new intermediate class for
>>>>> "vehicle with an engine" while deriving bicycles and gliders
>>>> >from the new internediate class for "vehicle without an
>>>>> engine".

>>
>>>>> The way you describe your problem it looks as if you started
>>>>> with the assumption that memory-mapped files are all the same,
>>>>> so having a single base class for them looked reasonable. But
>>>>> now you've recognized that the property of just being "memory-
>>>>> mapped" isn't what they all have in common. So revise your base
>>>>> class to contain only what's common to all of them and create
>>>>> intermediate classes you can derive from the more specialized
>>>>> cases where there aren't any issues when all the destructors
>>>>> are called.
>>>>> Regards, Jens

>>
>>>> Yes, I understand. I agree my conception was flawed. In C, you can
>>>> start with a flawed design such as this and make it work despite its
>>>> deficiencies. This illustrates that C++ is both more powerful and
>>>> more precise in its semantics than C. Thanks for your detailed and
>>>> helpful reply.

>>
>>> I don't mean to say that you can't simply make something flawed work
>>> in C++, as you can in C. C++ is just as flexible as C. The advantage
>>> of C++ over C in this regard is that it pragmatically encourages a
>>> coder to design and write clearly, and to some degree even promotes
>>> clear thinking.

>>
>> Generalizations you said and heard are both right and wrong. OOP is a mixed
>> blessing. C++ (and OOP) make you "design for future" and no one knows the
>> future. Changes in the requirements often alter what are "the right things" to
>> hide and expose; and then the cognitive biases and non-technical issues stand in
>> a way of re-designing the class structure all over again as it would be
>> appropriate "in an ideal world". The result is often much messier than in
>> procedural-oriented language. A case in point is Linux kernel: it is not only
>> because of its low-level nature is it beneficial that it is written in C rather
>> than C++: it is also because the kernel is extremely dynamic, the whole
>> subsystems are being fully re-written between minor versions *and their roles
>> and responsibilities (separation of duties) are changing at that*. OOP works
>> well when roles and responsibilities of base classes are relatively stable; this
>> is far from true in many subject areas.

>
> This cannot go unchallenged .
>
> What are saying here is that OOP is intrinsically more rigid than some
> other style, and therefore unsuited for something like an OS kernel,
> which is somehow more dynamic than something else.

Absolutely not. As you should have noticed I am avoiding generic statements
almost as a matter of principle (because generalizations is what allow people to
smuggle wrong notions under the cover of generally correct ideas).

I gave a very specific example: Linux Kernel. EPOC32 (now known as Symbian)
kernel and API was all natively-C++ (more accurately, a rather limited subset
thereof but with all traditional OO-features included). It has been a wonderful
little RTOS that pioneered lots of usable features of pen-based user interfaces
(currently evolved to touch-screens). The proof of its rigidity is simple: Linux
is well and kicking and the last holdout of Symbian consortium, Nokia (they are
the greatest mobile-phone producer hardware-wise, IMHO), is suffering major
losses and switching to (C-based AFAIK) Windows 8 only because they could not
upkeep now-theirs Symbian with all needed changes to stay competitive among
mobile users. And don't get me wrong: I believe Nokia guys are great in writing
high-quality software, too; it's just that C++-based Symbian was not greatly
helpful in achieving their goals.

As a former developer of apps for EPOC32 I can assure you that, from both
functionality and implementation quality, that RTOS beat xxx out of any other
RTOS alternative I evaluated at the time -- and I was younger and was seeking
perfection with more zeal than I do now. And, it did not have C++ stack usage
issue that someone (not you) mentioned at this thread (at the cost of one of
their C++ limitations I mentioned above -- quite annoying, but unrelated to the
OO features).

But.. programming for its C++ API was no fun at all. We had to spend lots of
energy to make Psion add a semi-usable clone of simple POSIX-like API.. and they
could never make it fully usable (not on my tenure, anyway) despite that their
native C++ system API was implemented with high quality and hardly had any bugs
(I do not remember finding any but maybe I was just lucky). Which, BTW proves
that seemingly simple and naive constructions of classic UNIX APIs are much
better thought of than it's often believed and require quite non-trivial
thinking to implement them correctly -- whereas using them is a piece of cake.

I do not want to get into a discussion of whether UNIX API is OO by its nature
(it very well may be and this does not contradict my position on OO because it's
been very stable, too -- and it's still relevant because it was designed from
the start by very smart people with great diligence; and the changes were
carefully designed, too) but it's worth noting that its native programming
language, for both API and implementation, is C -- which many people here
obviously regard as not OO enough.

> You are further
> making an argument that for such software all should be left out in
> the open, and that this will be more flexible.

Excuse me? You generalized my idea to the point I can't recognize it so I leave
it up to you to draw a connection between what I said and what you made out of
it -- after which I will be happy to point out where the confusion is coming from.

Well, yes, but the
> price of doing that is that it all turns into a thing commonly known
> as a "big ball of mud". That's it, really.

The biggest balls of mud I have seen so far (and I saw quite a lot) were written
in C++. I am not trying to theorize on why it is, just sharing the experience.

> There's another thing: you are mostly talking about information
> hiding, really.

Wrong. If anything, I was talking OO modeling. If you insist on generalities, I
was talking about how the real world is not just necessary different from our
models but how *unexpectedly different* it turns out when a new requirement
(that itself is more often than not, unexpected) comes and what troubles you
have when you incorporate another aspect of the infinitely complex world to your
model. I was also talking that these "accidents" are actually common and
inevitable because no one, even the best designer in the world, can know for
sure what aspect of this infinitely complex world s/he will be required to add
to their design next.

To narrow this a little bit, I specifically mentioned the design of class
hierarchies (it has little to do to hiding, isn't it?). Most of the time (and
always, when designing OO frameworks) a base class is designed for new classes
to be derived from it. To avoid acquisitions in tautology: I mean the new
classes that have to be designed AFTER the library is released. For frameworks
it's always true that some classes have to be derived and for other class
libraries it's very often true for their new versions. It happens, but
relatively rarely, that a hierarchy is "naturally closed".

When a new requirement arrives, it's often that "if you knew it from the
beginning" you would design base classes differently (meaning, you would like to
re-distribute their responsibilities and behaviors; you may also wish you had a
different set of base classes altogether). But re-designing base classes from
scratch is usually not an option for non-technical reasons, valid and/or subjective.

What it results with is that "the best in class" OO designer either:
a) "swallows it" and adds more stuff into their best classes -- this time with
little regard to the "right thing to do" (which would be, let me repeat myself,
to re-design the class structure from scratch).
b) departs from the company leaving it to others to clean the mess (and those
others would usually do same, but in even worse and less consistent manner than
the original OO designer would do unless they are wise and bold enough and have
enough clout to lead the business management to paying for the complete rewrite)

A technical consequence of the OO-model-patching described above is that the
derived classes (usually "the old" ones, introduced in former releases) acquire
state, code and dependencies that they ideally wouldn't need and start
performing less-than-optimally, the further the more so. I am not daring to even
call in this good community what those base classes become from OO point of view..

The above is, by the way, how what you called "a big ball of mud" is usually
constructed.

A procedural programmer in such situation will be expected just to write new
procedures and design new data structures -- which s/he would do after some
deliberation. When old procedures/structures are no longer used (to be
completely honest, this does not happen at once or very often, but after some
10-20 years and with only few of them), this can be detected automatically and
they can be retired without a big pomp -- if the firm has more or less decent
software lifetime governance.

> Well, if you fear rigidity, nothing stops you to leave
> all out in the open and be done with it.

I am not sure I follow these references to "all out in the open". The source
code shall be accessible to its maintainers, shan't it? -- otherwise, they would
not be able to even fix simplest bugs, let alone implementing a meaningful new
requirement.

As for, say, library users, I am not implying they have to have access to the
source code. Giving access to full definitions of data structures is sometimes
useful at low- and not-so-low levels; where it's not useful, it should be
avoided -- but I would not get out of my way at it. The thing is, where you need
a change it is often "the right way" to create a completely new data structure
rather than to force the old one into serving for the new purpose. I thought I
gave a good example of it discussing how FORTRAN matrices have had their data
structure "in the open" and stayed easily re-usable and extendable over decades
(and how C++ matrices with everything encapsulated have not been a match in
this regard).


>
> Goran.


-Pavel
 
Reply With Quote
 
Pavel
Guest
Posts: n/a
 
      12-11-2011
Stuart Redmann wrote:
> Sorry for interrupting your discussion
>
> On 5 Dez., Pavel wrote:
>> [snip].
>> OOP is a mixed blessing. C++ (and OOP) make you "design for future"
>> and no one knows the future. Changes in the requirements often alter
>> what are "the right things" to hide and expose; and then the cognitive
>> biases and non-technical issues stand in a way of re-designing the class
>> structure all over again as it would be appropriate "in an ideal world".
>> The result is often much messier than in procedural-oriented language.

>
>
> I found a nice page that might be of interest to you:
> http://reocities.com/tablizer/oopbad.htm

Thank you very much, it was a very interesting article: makes some valid points.
Slightly too extreme, to my taste; but otherwise, gives a lot of food for thought.

> There is also a very sweet observation about communism in it.

Even though what it tells about communism is more or less accurate, the analogy
is little lame: the communism does not work (in a sense that people would want
it to work that is; in some technical sense it does work -- but you don't want
to live in THAT society) because of faulty design (ignoring generally known
facts about human nature is only one of the flaws). On the other hand, where OO
works poorly, it does so due to unpredictability of change requirements. I would
not go so far to say applying OO is always wrong; I would not even say it is
always humanly possible to say whether OO is going to benefit a particular
project (all because of no one knows the future, even that of a simple software
project future).

Trying to deduce at least a semi-useful practical conclusion of the above:
business managers who try hard to explain "the big picture" to their software
teamd and fire those who does not listen are doing themselves a big favor. And
vice versa.

>
>
>> A case in point is Linux kernel: it is not only
>> because of its low-level nature is it beneficial that it is written in C rather
>> than C++: it is also because the kernel is extremely dynamic, the whole
>> subsystems are being fully re-written between minor versions *and their
>> roles and responsibilities (separation of duties) are changing at that*.
>> OOP works well when roles and responsibilities of base classes are
>> relatively stable; this is far from true in many subject areas.

>
>
> I thought that Linus didn't want to use C++ because C++ uses much more
> stack memory (which is quite limited in the kernel). Perhaps I should
> do some research...

Nah, Linus is finicky about many things, C++ included -- but he is a very smart
guy, so it will certainly entertain you to read what he really thinks of C++.
Just google "Linus Torvalds about C++" -- you won't miss *that* piece.

Stack size *is* an issue; but less of it these days; in older day, EPOC32
"Embedded C++" solved this issue by limiting exception mechanisms in quite
annoying matter (especially given their OS interface actually used exception)
but that limitation did not affect C++ OO-related features.

> However, even if we only used procedural programming under Linux, it
> would still be quite nice to use objects in the functions. Whole
> chapters in Linux tutorials are dedicated to the containers (mostly
> lists) inside the kernel. If Linux were written under C++, we would
> never have to read those chapters.

Yes and no.. intrusive lists that are mostly there certainly can be modeled in
C++ classes (in fact my recent projects involved such modeling); but, to be
completely honest, being in a position to both design and use these (what can be
less problematic), I cannot sincerely testify that "C++ way" brought up lots of
advantages with it (and it surely brought increased compilation time which is
soooooooo annoying and code bloating; quite moderate but I would not want it in
my kernel). I feel like I could live with C functions to manipulate lists.

But you touched on an IMHO fundamental question of how bad is exposing the dat
structure. My point is that exposing a really simple and reusable structure (I
remember I mentioned Linux kernel was very dynamic, but its lists in particular
are so fundamental and so simple that they stay mainly intact) is not always
evil (but you already know it from my FORTRAN matrix examples).

>
> I agree that many problem-spaces cannot be adequately structured by
> OOD, but that should not mean that we should design each and every sub-
> problem-space in an procedural way. The beauty of C++ is that we can
> use both design paradigms at the same time. Besides, C++ offers more
> than just OOD (think of generics).


I agree, of course. My personal complaints about C++ though is not that it has
OO-features but that it requires keeping bigger context in my head to understand
the meaning of given code; and thus I have less of it left to think of business
aspects of the problem at the same time. That is, the code

int i = 5;
f(i);
printf("%d\n", i);

in C always prints 5 (except if f is a macro but that would be a malicious code
that can be written in any langage). If it's in C++, you may have to sift
through quite a bit of headers to predict the printout. And the other complaint
is certainly the compilation time. Both drawbacks reduce the performance and
annoy programmers (and everyone knows annoying programmers who program for
living, not from academic interest, is not the smartest thing to do).

>
>
>> Sometimes even in well-defined areas programming with classes is more
>> limiting and creates less usable legacy than programming with
>> subroutines/functions only. A good example are matrix manipulation
>> libraries. If you choose to manipulate with matrices in an old-fashion
>> FORTRAN way (i.e. based on conventions about matrix internal
>> representation and adding functions perusing it which is a mortal sin
>> in OOP), your can reuse the libraries written as far back as in 70th
>> (NAG, IMSL, SSP etc) from your C++ or C program and all these are
>> data-compatible each with other. What I mean by that, you can allocate
>> and initialize internal representations (arrays) in C++, call some
>> subroutines from NAG, call SSP subroutines from SSP on the results,
>> then call your own C function you wrote decades later etc. all
>> without any extra copying or conversions.
>>
>> On the other hand, I have yet to see a pair of C++ matrix manipulation
>> libraries that could re-use one another's matrices without
>> copying/conversions.

> [snip]
>
> My co-worker spent quite some time to write a C++ wrapper for the BLAS
> library so that he can use operator+ and operator*. Such simple things
> make the code much more readable.

Maybe, but it's a mixed blessing, too (I know I overuse this expression .

Consider the future (I lived in this particular one) in which your friend's
matrices grow real big and they want to change the multiplication algorithm to
recursive Strassen to be easier on CPU. Now, recursive Strassen is
mathematically equivalent to the vanilla approach but from rounding perspective
s/he may be worried (and especially with big matrices) so suppose s/he designs a
modification that can keep the rounding error below the specified limit (e.g.,
by limiting the recursion depth). If s/he had a mulmat() procedure, s/he would
just add a "double tolerance" parameter to it (maybe with a default value or
function overload to protect old code -- for which C++ (but not OO) is quite
useful, for a change); but what is s/he going to do with operator* in this case?

My evil guess: if s/he he is going to meet this challenge far enough in the code
life, when the * has already been used in tens of generic algorithms, s/he will
add some "tolerance" hack to the matrix wrapper class. And, this is most
certainly not a good OO design: multiplication tolerance does not belong to a
matrix -- it belongs to the algorithm. The imperfection of the hack will lead to
many questions from peers and bugs: Just consider: which matrix, right or left
keeps the "right" tolerance to use.. what if the two are different.. how will
one propagate it to the results? Etc.. Etc..

In ideal world, you would model multiplication algorithm as a class -- but who
would do such a thing from the outset and what business manager would pay for it
from the outset?

I think this is another perfect example of inherent imperfection of modeling:
the root cause of the issue is that multiplying matrices is (in infinitely
complex "real world") quite different from multiplying numbers; but if the
difference was not important for the original requirements (or design, like in
this case), OO will entice designers into a "premature generalization" sin (in
this case -- adding generic algorithms that heavily depend on the overly simple
interface operator* and making it all but impossible to change it in the future).

Why can't we do that under C?
Maybe, for a good reason, like the one above?


>
> Regards,
> Stuart


Sincerely,
Pavel
 
Reply With Quote
 
Pavel
Guest
Posts: n/a
 
      12-11-2011
Werner wrote:
> On Dec 5, 9:36 am, Stuart Redmann<(E-Mail Removed)> wrote:
>> Sorry for interrupting your discussion
>>
>> On 5 Dez., Pavel wrote:
>>
>>> [snip].
>>> OOP is a mixed blessing. C++ (and OOP) make you "design for future"
>>> and no one knows the future. Changes in the requirements often alter
>>> what are "the right things" to hide and expose; and then the cognitive
>>> biases and non-technical issues stand in a way of re-designing the class
>>> structure all over again as it would be appropriate "in an ideal world".
>>> The result is often much messier than in procedural-oriented language.

>>
>> I found a nice page that might be of interest to you:http://reocities.com/tablizer/oopbad.htm
>> There is also a very sweet observation about communism in it.

>
> I did not read all the oopbad stuff, but I've seen a lot
> of bad code due to programming in OOP languages without OOD.
>
> I've also found that using good OOD in conjunction templates
> allows one to reuse and share code. Especially SRP (Single
> Responsibility Principle).
>
> I've also noticed that many a procedural programming
> uses some form of pseudo OOD to organize his code, in
> fact - almost all of the ones I've seen do this (Even
> if this means using some directory structure or file
> for related functions).
>
> OOD gives us the means to factorize the
> organization/architecture into byte sizes, heh?
>
> OT:
>
> About communism (and maybe this is your point too):
>
> Perfect communism would make perfect sense.

Perfect communism with real people (and I do not mean leaders; those actually
had to be perfect-for-communism) did exist. Maoism/Stalinism/Khmer rogue regimes
are good examples.

Perfect communism with perfect-for-communism people does make logical sense but
be careful in you wishes (even after forgetting about implementation issues).
There are plentiful examples of such communism in the Nature: ants in ant house,
bees in a heave, cells in your body. Would you care to become either?


> As reference
> you can consider the early church in Acts (The Bible).
> Perfect OOP perhaps too? Yes, early 20th century to
> modern day communism have capitalist leaders, hence it
> does not make sense . They are into "self-enrichment",
> and living at the cost of others. Perhaps many OOD
> programs have C programmers running the design (with
> respect)?

Or perhaps OOD is just slightly less practical than it is often thought of and
advertised?

>
> Capitalism...
>
> - Will evolve something terrible yet, the consequences
> far worse (global, perhaps catastrophical).

And communism would never do such a thing, would it?

>
> Regards,
>
> Werner
>
>


Regards,

-Pavel
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: Rationale for base class pure virtual function call fromctor/dtor being undefined behaviour. gwowen C++ 6 01-18-2012 07:14 PM
Derived::Derived(const Base&) and Derived& operator=(const Base&) developereo@hotmail.com C++ 1 05-23-2007 01:44 PM
Derived::Derived(const Base&) and Derived& operator=(const Base&) developereo@hotmail.com C++ 1 05-23-2007 12:07 AM
Ctor/Dtor order Karl Heinz Buchegger C++ 3 07-30-2003 01:22 PM
Explicit ctor/dtor calls Jacques Labuschagne C++ 3 07-20-2003 04:06 PM



Advertisments