Re: only call dtor of derived class?

Discussion in 'C++' started by Jens Thoms Toerring, Dec 3, 2011.

  1. Heck <> 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://toerring.de
     
    Jens Thoms Toerring, Dec 3, 2011
    #1
    1. Advertising

  2. Jens Thoms Toerring

    Pavel Guest

    Heck wrote:
    > On Sat, 03 Dec 2011 22:07:56 -0500, Heck
    > <> wrote:
    >
    >> On 3 Dec 2011 00:32:35 GMT, (Jens Thoms Toerring)
    >> wrote:
    >>
    >>> Heck<> 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
     
    Pavel, Dec 5, 2011
    #2
    1. Advertising

  3. 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
     
    Stuart Redmann, Dec 5, 2011
    #3
  4. Jens Thoms Toerring

    Werner Guest

    On Dec 5, 9:36 am, 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
    > 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
     
    Werner, Dec 5, 2011
    #4
  5. 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
     
    Stuart Redmann, Dec 5, 2011
    #5
  6. Pavel <> 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.
     
    Juha Nieminen, Dec 5, 2011
    #6
  7. Jens Thoms Toerring

    Goran Guest

    On Dec 5, 4:07 am, Pavel
    <> wrote:
    > Heck wrote:
    > > On Sat, 03 Dec 2011 22:07:56 -0500, Heck
    > > <>  wrote:

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

    >
    > >>> Heck<>  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.
     
    Goran, Dec 5, 2011
    #7
  8. Jens Thoms Toerring

    Pavel Guest

    Goran wrote:
    > On Dec 5, 4:07 am, Pavel
    > <> wrote:
    >> Heck wrote:
    >>> On Sat, 03 Dec 2011 22:07:56 -0500, Heck
    >>> <> wrote:

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

    >>
    >>>>> Heck<> 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
     
    Pavel, Dec 11, 2011
    #8
  9. Jens Thoms Toerring

    Pavel Guest

    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
     
    Pavel, Dec 11, 2011
    #9
  10. Jens Thoms Toerring

    Pavel Guest

    Werner wrote:
    > On Dec 5, 9:36 am, 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
    >> 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
     
    Pavel, Dec 11, 2011
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    1
    Views:
    425
    myork
    May 23, 2007
  2. Replies:
    1
    Views:
    412
    Victor Bazarov
    May 23, 2007
  3. David
    Replies:
    3
    Views:
    435
    Grizlyk
    Jan 29, 2008
  4. Volkan YAZICI
    Replies:
    4
    Views:
    247
    Öö Tiib
    Apr 24, 2011
  5. gwowen
    Replies:
    6
    Views:
    341
    MikeWhy
    Jan 18, 2012
Loading...

Share This Page