Re: Confused about non-virtual functions

Discussion in 'C++' started by David White, Jun 24, 2003.

  1. David White

    David White Guest

    Robert William Vesterman <> wrote in message
    news:...
    > I'm confused about the purpose of non-virtual functions. As I
    > understand it, if you have a class E that extends a class B and
    > overrides a non-virtual function f(), then the f() that actually gets
    > called for an E object depends upon whether that E object is known as
    > an E or as a B at the time of the call.


    Here 'known as' means the declared type where the call is made, I take it.

    Non-virtual functions are overwhelmingly those that are not overridden at
    all. Their purpose is to do something that is not for overriding. Virtual
    function calls require indirection, for which there is likely to be a
    performance penalty, a penalty that is unnecessary if the function is not to
    be overridden.

    > I don't understand what's useful about this ambiguity in what the f()
    > of an E is. Could someone please give me an example of why it would
    > be useful?


    I can't think of one. I can't think of a circumstance in which I would
    override a non-virtual function.

    > I don't mean "useful as opposed to virtual functions". I mean "useful
    > as opposed to not allowing non-virtual functions to be overridden in
    > the first place".


    I would suggest that a compiler issue a warning, at least, whenever a
    non-virtual function is overridden.

    David
    David White, Jun 24, 2003
    #1
    1. Advertising

  2. David White wrote:
    >
    > Robert William Vesterman <> wrote in message
    > news:...
    > > I'm confused about the purpose of non-virtual functions. As I
    > > understand it, if you have a class E that extends a class B and
    > > overrides a non-virtual function f(), then the f() that actually gets
    > > called for an E object depends upon whether that E object is known as
    > > an E or as a B at the time of the call.

    >
    > Here 'known as' means the declared type where the call is made, I take it.
    >
    > Non-virtual functions are overwhelmingly those that are not overridden at
    > all. Their purpose is to do something that is not for overriding. Virtual
    > function calls require indirection, for which there is likely to be a
    > performance penalty, a penalty that is unnecessary if the function is not to
    > be overridden.
    >
    > > I don't understand what's useful about this ambiguity in what the f()
    > > of an E is. Could someone please give me an example of why it would
    > > be useful?

    >
    > I can't think of one. I can't think of a circumstance in which I would
    > override a non-virtual function.
    >


    Very simple.
    Imagine you have a class: Base
    This class has member functions. In order to simplify things you decide
    to factor out the common code and create a helper function for that:

    class Base
    {
    void foo() { ...; Helper(); ... }
    void foo2() { ...; Helper(); ... }

    void Helper();
    };

    Now you derive a class from Base: Derived
    It does the same thing: It has some functions and in order to
    simplify things you factor out common code and put it into an Helper
    function - which by accident gets the very same name as the
    one in the base class.

    class Derived : public Base
    {
    void foo3() { ...; Helper(); ... }
    void foo4() { ...; Helper(); ... }

    void Helper();
    }

    Now: If the correct job for Base::foo() and Base::foo2() is to use
    Base::Helper() then you would be very surprised if Derived::Helper()
    is called. If on the other hand the correct job for Base::foo()
    and Base::foo2() is to use the Helper() from the most derived
    class, then you declare Helper() as being virtual and be happy
    with using polymorphism to do the job.

    --
    Karl Heinz Buchegger
    Karl Heinz Buchegger, Jun 24, 2003
    #2
    1. Advertising

  3. David White

    David White Guest

    "Karl Heinz Buchegger" <> wrote in message
    news:...
    >
    >
    > David White wrote:
    > >
    > > I can't think of one. I can't think of a circumstance in which I would
    > > override a non-virtual function.
    > >

    >
    > Very simple.
    > Imagine you have a class: Base
    > This class has member functions. In order to simplify things you decide
    > to factor out the common code and create a helper function for that:
    >
    > class Base
    > {
    > void foo() { ...; Helper(); ... }
    > void foo2() { ...; Helper(); ... }
    >
    > void Helper();
    > };
    >
    > Now you derive a class from Base: Derived
    > It does the same thing: It has some functions and in order to
    > simplify things you factor out common code and put it into an Helper
    > function - which by accident gets the very same name as the
    > one in the base class.
    >
    > class Derived : public Base
    > {
    > void foo3() { ...; Helper(); ... }
    > void foo4() { ...; Helper(); ... }
    >
    > void Helper();
    > }
    >
    > Now: If the correct job for Base::foo() and Base::foo2() is to use
    > Base::Helper() then you would be very surprised if Derived::Helper()
    > is called. If on the other hand the correct job for Base::foo()
    > and Base::foo2() is to use the Helper() from the most derived
    > class, then you declare Helper() as being virtual and be happy
    > with using polymorphism to do the job.


    Well, I wouldn't do it. It's confusing and it's asking for trouble. Someone
    not so familiar with the code might notice 'Helper' in the derived class's
    interface, and later see the calls to 'Helper' in the base class function
    bodies and assume that it's a virtual call. You might have the completely
    wrong understanding of what's happening simply because you failed to notice
    that there is no 'virtual' on the base-class declaration of 'Helper'().
    Sorry, but I think this is ghastly.

    David
    David White, Jun 24, 2003
    #3
  4. David White wrote:
    >
    >
    > Well, I wouldn't do it.


    If you are working with a class framework you might do it
    without even noticing that you do it.

    > It's confusing and it's asking for trouble. Someone
    > not so familiar with the code might notice 'Helper' in the derived class's
    > interface, and later see the calls to 'Helper' in the base class function
    > bodies and assume that it's a virtual call. You might have the completely
    > wrong understanding of what's happening simply because you failed to notice
    > that there is no 'virtual' on the base-class declaration of 'Helper'().


    If one analyzes code that sloppy he/she should stop
    immediatly using C++

    --
    Karl Heinz Buchegger
    Karl Heinz Buchegger, Jun 24, 2003
    #4
  5. David White wrote:
    >
    > David White <> wrote in message
    > news:BuNJa.7667$...
    >
    > [all snipped]
    >
    > In light of Victor's post, interpret 'overridden' and 'override' in my post
    > more loosely than usual. By 'override' I meant adding a function of the same
    > name with matching arguments in a derived class. Of course, Victor is right
    > that if you do this to a non-virtual function then you are not really
    > overriding it.


    This is called 'hiding'... 'scoping', you know.

    regards,
    alexander.
    Alexander Terekhov, Jun 24, 2003
    #5
  6. David White

    David White Guest

    Karl Heinz Buchegger <> wrote in message
    news:...
    >
    >
    > David White wrote:
    > >
    > >
    > > Well, I wouldn't do it.

    >
    > If you are working with a class framework you might do it
    > without even noticing that you do it.


    More grounds for the compiler giving a warning.

    > > It's confusing and it's asking for trouble. Someone
    > > not so familiar with the code might notice 'Helper' in the derived

    class's
    > > interface, and later see the calls to 'Helper' in the base class

    function
    > > bodies and assume that it's a virtual call. You might have the

    completely
    > > wrong understanding of what's happening simply because you failed to

    notice
    > > that there is no 'virtual' on the base-class declaration of 'Helper'().

    >
    > If one analyzes code that sloppy he/she should stop
    > immediatly using C++


    I disagree. In real code I've never seen the same function in a derived
    class that hadn't been declared 'virtual' in the base class. I've never
    heard of the practice written about, or recommended anywhere. I don't
    believe it's a common practice. If you can convince me otherwise then I'll
    change my mind. If this practice is so rare that few programmers would
    expect to come across it, then misunderstandings are likely. To call it
    "sloppy", as though anyone who isn't perfect at all times shouldn't be
    programming is just bullshit. Mistakes happen. If they didn't then all
    software would be bug-free. I just think that using a practice that few
    would expect, and which isn't necessary, is going to make mistakes more
    likely. But, as I said, if you can convince me that I'm wrong about the
    frequency of this practice then I'll change my mind.

    David
    David White, Jun 25, 2003
    #6
  7. David White wrote:
    >
    > >
    > > If one analyzes code that sloppy he/she should stop
    > > immediatly using C++

    >
    > I disagree. In real code I've never seen the same function in a derived
    > class that hadn't been declared 'virtual' in the base class.


    It is used sometimes for private/protected helper functions when
    refactoring code. Most of the time it simply happens without the
    programmer even noticing it.

    > I've never
    > heard of the practice written about, or recommended anywhere. I don't
    > believe it's a common practice. If you can convince me otherwise then I'll
    > change my mind. If this practice is so rare that few programmers would
    > expect to come across it, then misunderstandings are likely. To call it
    > "sloppy", as though anyone who isn't perfect at all times shouldn't be
    > programming is just bullshit.


    I haven't said that anyone who isn't perfect should stop programming.
    I said: anyone who wines around not noticing that a function is
    virtual should stop.

    > Mistakes happen.


    :)
    You don't have to tell me.
    Nevertheless: Doing a code analyze based on assumptions (which is
    ok) and then figuring out that the assumptions didn't hold
    and blaming the language for that ....

    And yes, I have done lots of foreign code analyzes. Figuring out
    what was virtual and what was not, has never been a problem.

    > If they didn't then all
    > software would be bug-free. I just think that using a practice that few
    > would expect, and which isn't necessary, is going to make mistakes more
    > likely. But, as I said, if you can convince me that I'm wrong about the
    > frequency of this practice then I'll change my mind.
    >
    > David


    --
    Karl Heinz Buchegger
    Karl Heinz Buchegger, Jun 25, 2003
    #7
  8. David White

    David White Guest

    Robert Groves <> wrote in message
    news:...
    > On Tue, 24 Jun 2003 20:23:17 +1000, "David White" <no.email@provided>
    > wrote:
    > >Well, I wouldn't do it. It's confusing and it's asking for trouble.

    Someone
    > >not so familiar with the code might notice 'Helper' in the derived

    class's
    > >interface, and later see the calls to 'Helper' in the base class function
    > >bodies and assume that it's a virtual call. You might have the completely
    > >wrong understanding of what's happening simply because you failed to

    notice
    > >that there is no 'virtual' on the base-class declaration of 'Helper'().
    > >Sorry, but I think this is ghastly.
    > >


    > Of course thats what you think, you're a java or maybe c-sharp
    > programmer. The point is that this is normal in C++.


    Never written one character of Java or C#. I wouldn't touch C# with a barge
    pole, simply because it's a Microsoft proprietary language.

    In chronological order, the languages I've used are: a Basic-like language,
    assembler, C, C++. I've been using C++ for about 8 years, always in OO
    designs, and I disagree with you that "this is normal in C++". In my
    experience - meaning code that I've written, my colleagues have written, and
    examples in books and magazines - I am yet to come across non-virtual usage
    of identical functions in base and derived classes. Maybe I'm too sheltered,
    but that's my experience.

    pBase = pDerived;
    pBase->func();
    pDerived->func();

    If you find that, say, 99.9% of the time a function call through an object
    pointer calls the same function, regardless of which class the pointer is
    declared to point to (i.e., the function is virtual), then when you see such
    calls in code you are naturally going to expect them to be virtual calls.

    Also, I just find it instinctively objectionable that a _different_ function
    will be called depending on whether you declare the _same_ object as a Base*
    or Derived*. I just find it too subtle and contrary to the promise of
    polymorphic behaviour in an OO design that what happens when you call a
    particular function (or, in OO terms, send a particular message) depends on
    what type you _declare_ an object to be, rather than what type the object
    _is_.

    David
    David White, Jun 27, 2003
    #8
  9. "David White" <> wrote...
    > [...]
    > Also, I just find it instinctively objectionable that a _different_

    function
    > will be called depending on whether you declare the _same_ object as a

    Base*
    > or Derived*. [...]


    You've been writing C++ for "about 8 years" and still cannot
    understand one simple thing: it's NOT the same object. Once
    you manage to comprehend this, you'll understand why calling
    a function with the same name _should_ be different _unless_
    it's a virtual function.

    To help you understand what it means "not the same object",
    please run this:

    #include <iostream>

    struct A {
    double foo;
    };

    struct B {
    int a;
    void func() {
    std::cout << (void*)this << std::endl;
    }
    };

    struct D : A, B {
    void func() {
    std::cout << (void*)this << std::endl;
    }
    };

    int main() {
    D d;
    D *pd = &d;
    B *pb = &d;
    void *p = pd;
    std::cout << "As D : " << p << std::endl;
    p = pb;
    std::cout << "As B : " << p << std::endl;
    pd->func();
    pb->func();
    }

    Does it output the same address? If it doesn't, how can
    "the same object" have different addresses?

    Think about it before you reply next time.

    Victor
    Victor Bazarov, Jun 27, 2003
    #9
  10. David White

    David White Guest

    Victor Bazarov <> wrote in message
    news:...
    > "David White" <> wrote...
    > > [...]
    > > Also, I just find it instinctively objectionable that a _different_

    > function
    > > will be called depending on whether you declare the _same_ object as a

    > Base*
    > > or Derived*. [...]

    >
    > You've been writing C++ for "about 8 years" and still cannot
    > understand one simple thing: it's NOT the same object. Once
    > you manage to comprehend this, you'll understand why calling
    > a function with the same name _should_ be different _unless_
    > it's a virtual function.
    >
    > To help you understand what it means "not the same object",
    > please run this:


    My argument was in the context of the example that _I_ gave. Why give a
    completely different one?

    class Base
    {
    public:
    void func();
    //...
    };

    class Derived : public Base
    {
    public:
    void func();
    //...
    };

    void f()
    {
    Derived d;
    Base *pBase = &d;
    Derived *pDerived = &d;
    pBase->func();
    pDerived->func();
    }

    Aside from the pointers, how many objects have been created here? One, a
    Derived. That's all.

    Nevertheless, I'll look at yours.

    > #include <iostream>
    >
    > struct A {
    > double foo;
    > };
    >
    > struct B {
    > int a;
    > void func() {
    > std::cout << (void*)this << std::endl;
    > }
    > };
    >
    > struct D : A, B {
    > void func() {
    > std::cout << (void*)this << std::endl;
    > }
    > };
    >
    > int main() {
    > D d;


    Only one object here, a D.

    > D *pd = &d;


    Points to the D part of d, which of course includes the A and B parts, which
    it inherits.

    > B *pb = &d;


    Points to the B part of d, which might or might not have the same address as
    the D part. In any case, we are still pointing to a D, because that's the
    type that d is.

    > void *p = pd;


    Converts the address of d to void*, with who-knows what result.

    > std::cout << "As D : " << p << std::endl;
    > p = pb;
    > std::cout << "As B : " << p << std::endl;
    > pd->func();
    > pb->func();
    > }
    >
    > Does it output the same address? If it doesn't, how can
    > "the same object" have different addresses?


    Because you are pointing to different parts of the _same_ object. So what?

    David
    David White, Jun 27, 2003
    #10
  11. David White

    David White Guest

    Victor Bazarov <> wrote in message
    news:HPNKa.27749$Ab2.52246@sccrnsc01...
    > "David White" <> wrote...
    > > My argument was in the context of the example that _I_ gave. Why give a
    > > completely different one?

    >
    > Because when you started arguing, there was no example. Why
    > should the language behave differently with different examples?
    > There would be no consistency, no logic.
    >
    > >
    > > class Base
    > > {
    > > public:
    > > void func();
    > > //...
    > > };
    > >
    > > class Derived : public Base
    > > {
    > > public:
    > > void func();
    > > //...
    > > };
    > >
    > > void f()
    > > {
    > > Derived d;
    > > Base *pBase = &d;
    > > Derived *pDerived = &d;
    > > pBase->func();
    > > pDerived->func();
    > > }
    > >
    > > Aside from the pointers, how many objects have been created here? One, a
    > > Derived. That's all.

    >
    > No, there are two objects. One inside the other. B inside D.
    > As soon as you stop being so irrationally stubborn, you might
    > actually begin to understand...


    There is ONE object. One D. That's what you created.

    > >
    > > Nevertheless, I'll look at yours.

    >
    > Why, thank you.
    >
    > >
    > > > #include <iostream>
    > > >
    > > > struct A {
    > > > double foo;
    > > > };
    > > >
    > > > struct B {
    > > > int a;
    > > > void func() {
    > > > std::cout << (void*)this << std::endl;
    > > > }
    > > > };
    > > >
    > > > struct D : A, B {
    > > > void func() {
    > > > std::cout << (void*)this << std::endl;
    > > > }
    > > > };
    > > >
    > > > int main() {
    > > > D d;

    > >
    > > Only one object here, a D.

    >
    > Well, no. One D. And inside it there are four more. One A
    > (and inside it a double), one B (and an int inside it).


    Wrong. There is only a D.

    > >
    > > > D *pd = &d;

    > >
    > > Points to the D part of d, which of course includes the A and B parts,

    > which
    > > it inherits.

    >
    > "Parts"? Are they not objects?


    No, they are not. Suppose you have a class Shape, which is abstract, and
    from it you derive a class Ellipse. If you have a Shape* that points to an
    Ellipse, you are not pointing to a Shape object, because there is no such
    thing. You are pointing to the Shape part of an Ellipse. After all, that's
    what you made, an Ellipse. Further, I suggest that on most implementations
    the Shape* and Ellipse* would be the same address. Does that mean, according
    to your own reasoning, that they point to the same object?

    > >
    > > > B *pb = &d;

    > >
    > > Points to the B part of d, which might or might not have the same

    address
    > as
    > > the D part. In any case, we are still pointing to a D, because that's

    the
    > > type that d is.

    >
    > Nope. It's what is known as a "B subobject of D". Whether you call
    > it "part" or otherwise, doesn't matter. You are not "still pointing
    > to a D".


    Yes, you are. d is a D, so you are pointing to a D.

    > You're pointing to a different object. Repeat after me:
    > "pb points to a different object".


    I'm not going to repeat something that's wrong. I suggest you repeat this:
    "pb points to the same object"

    > If you convert a D* into A*, does
    > it still point to the "same object"?


    Yes, it still points to d, which is a D. To prove that it does, add a
    virtual function to A that is overridden in D. Whether you call the function
    through an A* or a D* the result is the same: it calls the function in D.
    That's because the object you are pointing to is a D.

    > Are you able to pass A* where
    > B* is expected? No. Why? Because they are not the same object.
    >
    > >
    > > > void *p = pd;

    > >
    > > Converts the address of d to void*, with who-knows what result.

    >
    > The Standard know what result is. If I convert it to void*
    > and then back to D*, the value doesn't change. You seem to be
    > in need of a good C++ study, mate.
    >
    > >
    > > > std::cout << "As D : " << p << std::endl;
    > > > p = pb;
    > > > std::cout << "As B : " << p << std::endl;
    > > > pd->func();
    > > > pb->func();
    > > > }
    > > >
    > > > Does it output the same address? If it doesn't, how can
    > > > "the same object" have different addresses?

    > >
    > > Because you are pointing to different parts of the _same_ object. So

    what?
    >
    > You're starting to get there.


    Being a sarcastic arsehole generally doesn't facilitate constructive
    discussions.

    > I can understand how you don't want to give up your mole-hill.


    I've expressed an _opinion_ as to the usage of identical, non-virtual
    functions in base and derived classes, and I've given my reasons.

    > "I did C++ for 8 years and I know that it's full of it, all wrong
    > and [how did you put it?... Ah] intuitively objectionable".


    Instinctively. And I was referring only to the usage we are discussing.
    Where did "I know that it's full of it, all wrong" come from? Not from me,
    even though you've presented this crap as a quote from me.

    I said that I had not seen the usage we are discussing in 8 years of
    programming C++. I neither stated nor implied anything else. I even
    qualified what I've come across in that time with "Maybe I'm too sheltered,
    but that's my experience." Perhaps you can explain how you managed to turn
    that into the bullshit above.

    > Give it up. You have still a significant part of the language to learn.


    I won't disagree with that, but it doesn't include the nonsense you've been
    saying in your last couple of posts.

    > Try not to think that what your "instinct" is telling you is the
    > ultimate truth.


    I've said nothing about the "ultimate truth". I've been expressing an
    opinion on a specific usage.

    > Let your "instinct" grow with you as you study the
    > language instead of objecting to its features.


    My instinct can say what it likes. I am entitled to dislike a usage that is
    easily missed because it's rare and contradicts the concept of IS-A in OO
    programming.

    David
    David White, Jun 27, 2003
    #11
  12. David White wrote:
    [...]
    > Being a sarcastic arsehole generally doesn't facilitate constructive
    > discussions.


    So next time try to not waste your time and common bandwidth
    "discussing" something with Bazarov.

    "Objects can contain other objects, called sub-objects. A sub-object
    can be a member sub-object (9.2), a base class sub-object (clause 10),
    or an array element. An object that is not a sub-object of any other
    object is called a complete object."

    regards,
    alexander.
    Alexander Terekhov, Jun 27, 2003
    #12
    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. Victor Bazarov
    Replies:
    18
    Views:
    2,607
    David Cattarin
    Jun 27, 2003
  2. tom_usenet
    Replies:
    0
    Views:
    872
    tom_usenet
    Jun 24, 2003
  3. David Cattarin
    Replies:
    0
    Views:
    773
    David Cattarin
    Jun 24, 2003
  4. Michael Winter
    Replies:
    9
    Views:
    616
    Michael Winter
    Sep 24, 2003
  5. John Goche
    Replies:
    10
    Views:
    741
    Marcus Kwok
    Dec 8, 2006
Loading...

Share This Page