Polymorphism without virtual in C++

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • feel

    Polymorphism without virtual in C++

    Hi,All
    I am sure it's an old question. But I just find a interesting design
    about this: Polymorphism without virtual function in a C++ class.
    My solution is for some special case, trust me, very special.
    1 single root class tree
    2 the leaf(lowest level) classes are sealed which means we should not
    inherite class from them.
    3 PImpl idiom. There is only one data mumber in root class and there
    is no any other data mumber in child class and virtual funtions.

    In my solution, the destructor of root class is not virtual, but we
    can use base class pointer to point derived class object.

    My question is: is this design follow the C++ standard? I tested it in
    VS2005. it's ok. How about GCC?I remember that this non-virtual
    destructor behavor is undefine in C++ standard.

    Here is a simple code example:

    class base
    {
    public:
    ~base()
    {
    delete[] p;
    };

    protected:
    int *p;

    base():p(new int[10])
    {
    };

    base(int *pp) : p(pp)
    {
    };
    };

    class base1 : public base
    {
    protected:
    base1()
    {
    };
    };

    class my : public base1
    {
    public:
    my ()
    {
    p = new int[10];
    };
    };

    int _tmain(int argc, _TCHAR* argv[])
    {

    base1 *o = new my;

    delete o;
    return 0;
    }
  • Victor Bazarov

    #2
    Re: Polymorphism without virtual in C++

    feel wrote:
    Hi,All
    I am sure it's an old question. But I just find a interesting design
    about this: Polymorphism without virtual function in a C++ class.
    My question is, "why?"
    My solution is for some special case, trust me, very special.
    1 single root class tree
    2 the leaf(lowest level) classes are sealed which means we should not
    inherite class from them.
    3 PImpl idiom. There is only one data mumber in root class and there
    is no any other data mumber in child class and virtual funtions.
    >
    In my solution, the destructor of root class is not virtual, but we
    can use base class pointer to point derived class object.
    >
    My question is: is this design follow the C++ standard? I tested it in
    VS2005. it's ok. How about GCC?I remember that this non-virtual
    destructor behavor is undefine in C++ standard.
    Correct. It's undefined behaviour to delete the derived class object
    through the base class pointer in the absence of a virtual destructor.
    >
    Here is a simple code example:
    >
    class base
    {
    public:
    ~base()
    {
    delete[] p;
    };
    Drop the semicolons after all the function bodies. They are superfluous
    (although not an error).
    >
    protected:
    int *p;
    >
    base():p(new int[10])
    {
    };
    >
    base(int *pp) : p(pp)
    Need a comment here that 'base' takes ownership of the pointer passed to
    it. Also, probably want to declare this c-tor "explicit".
    {
    };
    };
    >
    class base1 : public base
    {
    protected:
    base1()
    {
    };
    };
    >
    class my : public base1
    {
    public:
    my ()
    {
    p = new int[10];
    Memory leak here.
    };
    };
    >
    int _tmain(int argc, _TCHAR* argv[])
    There is no standard function '_tmain'. There is no standard type
    '_TCHAR'. You must have forgotten to include the proper header[s].
    Don't check your code with VC++ without turning off extensions.
    {
    >
    base1 *o = new my;
    >
    delete o;
    Ka-boom! Undefined behaviour.
    return 0;
    }
    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask

    Comment

    • Joe Greer

      #3
      Re: Polymorphism without virtual in C++

      Victor Bazarov <v.Abazarov@com Acast.netwrote in news:g7cmb5$6so $1
      @news.datemas.d e:
      >{
      >>
      > base1 *o = new my;
      >>
      > delete o;
      >
      Ka-boom! Undefined behaviour.
      >
      Not necessarily Ka-boom, you might get nasal demons instead.

      Seriously though, the problem is that if you add any member variables to
      your derived class, Base' destructor won't know how to clean them up and
      will probably only return a fraction of the memory to the heap. The rest
      would be left dangling... holding onto file handles, memory, mutexes... IOW
      nasal demons. In this very simple case, the undefined behavior may look
      like its working, but it wouldn't take much before that changed and it did
      something very hard to debug because, of course, the bug would show up
      elsewhere as some random behavior.

      joe

      Comment

      • Juha Nieminen

        #4
        Re: Polymorphism without virtual in C++

        feel wrote:
        I am sure it's an old question. But I just find a interesting design
        about this: Polymorphism without virtual function in a C++ class.
        I really can't see where the polymorphic part of your code is.

        You *can* create some kind of polymorphism without using the keyword
        'virtual', for example by using member function pointers (rather than
        virtual functions), but that doesn't really make too much sense, as
        putting member pointers in the class is only counter-productive and
        doesn't achieve anything 'virtual' wouldn't.
        3 PImpl idiom. There is only one data mumber in root class and there
        is no any other data mumber in child class and virtual funtions.
        So there's a pointer to dynamically allocated data in your base class.
        Exactly which part of this is polymorphic?

        (Personally I really can't understand what's all the fuss about the
        Pimpl idiom. It only makes classes consume more memory and slower, which
        can be especially counterproducti ve with small classes which have to be
        instantiated frequently and in large amounts. The only situation where
        there may be an advantage is if the class is large, it's copied around a
        lot, and the Pimpl data is reference-counted or CoW'ed.)
        In my solution, the destructor of root class is not virtual, but we
        can use base class pointer to point derived class object.
        The base class destructor can destroy the data in the base class, yes.
        I still fail to see the polymorphic part.

        The real problem, as presented by others, is that if you ever add
        anything in a derived class that needs to be destroyed, deleting the
        object through a base-class pointer will most probably not destroy the
        data in the derived class.

        (Technically speaking deleting through a base class pointer without a
        virtual destructor is undefined behavior even if the derived class is
        empty, but I suppose most compilers will do what you expect.)
        class base
        {
        public:
        ~base()
        {
        delete[] p;
        };
        >
        protected:
        int *p;
        >
        base():p(new int[10])
        {
        };
        >
        base(int *pp) : p(pp)
        {
        };
        };
        You probably shortened the example for the sake of brevity, but a few
        comments nevertheless:

        - Allocating unmanaged memory in the initialization list of the
        constructor is asking for trouble.

        - If objects of this class are ever copied or assigned, problems will
        happen.

        - Deleting a pointer given to the constructor is dubious practice at
        best. You can't know what it's pointing to.
        class base1 : public base
        {
        protected:
        base1()
        {
        };
        };
        I didn't quite understand what's the purpose of this class. It does
        nothing 'base' wouldn't already do.
        class my : public base1
        {
        public:
        my ()
        {
        p = new int[10];
        };
        };
        This is a good example of why member variables in the protected
        section are as bad as in the public section. You are assigning something
        to 'p' without any regard to what it might have. In this case it has
        already memory allocated to it, which is leaked by your assignment.

        Comment

        • feel

          #5
          Re: Polymorphism without virtual in C++

          My code is a bad example ~_^. But if you can check with AcGe classes
          in ObjectARX(from AutoDesk's AutoCAD sdk), you will see the same
          design. The special case is:
          1 for geometry class, esp for leaf classes, we donot want user to
          extend it. if you want to add other kind of geometry type, please add
          new class.The leaf of class tree is 'sealed' class.
          2 Polymorphism in this design is the Impl pointer in root class. we
          can implementation the real version class for every leaf class.
          3 Except for leaf class, every class can not be initialize in the
          stack.in my example code, you can not do this:
          base1 b;

          Why? because I donot want a big class in my header file. For geometry
          classes,such as AcGeNurbCurve, there are many method we have to put
          into interface. but we can split all these method into different
          catalog: 3dentity, curve, spline curve. Then you will see a class
          tree. There are small number methods in every node(classes in the
          tree). And it's meaningfull for a geometry class library. we may do
          something for all kind of curves,right? so if we use Pimpl idiom, you
          can find Polymorphism.

          Comment

          • James Kanze

            #6
            Re: Polymorphism without virtual in C++

            On Aug 6, 7:40 pm, Joe Greer <jgr...@doublet ake.comwrote:
            Victor Bazarov <v.Abaza...@com Acast.netwrote in news:g7cmb5$6so $1
            @news.datemas.d e:
            {
            base1 *o = new my;
            delete o;
            Ka-boom! Undefined behaviour.
            Not necessarily Ka-boom, you might get nasal demons instead.
            Seriously though, the problem is that if you add any member
            variables to your derived class, Base' destructor won't know
            how to clean them up and will probably only return a fraction
            of the memory to the heap.
            The problem here is that it is undefined behavior, and that
            anything can happen. I can't think of a reasonalble
            implementation where only part of the memory is freed, but I can
            certainly think of cases where it will corrupt the free space
            arena, and cause the program to crash, either immediately, or
            sometime later.

            --
            James Kanze (GABI Software) email:james.kan ze@gmail.com
            Conseils en informatique orientée objet/
            Beratung in objektorientier ter Datenverarbeitu ng
            9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

            Comment

            • James Kanze

              #7
              Re: Polymorphism without virtual in C++

              On Aug 7, 1:59 am, Juha Nieminen <nos...@thanks. invalidwrote:
              feel wrote:
              (Personally I really can't understand what's all the fuss
              about the Pimpl idiom. It only makes classes consume more
              memory and slower, which can be especially counterproducti ve
              with small classes which have to be instantiated frequently
              and in large amounts. The only situation where there may be an
              advantage is if the class is large, it's copied around a lot,
              and the Pimpl data is reference-counted or CoW'ed.)
              It reduces coupling, sometimes enormously.
              In my solution, the destructor of root class is not virtual, but we
              can use base class pointer to point derived class object.
              The base class destructor can destroy the data in the base class, yes.
              I still fail to see the polymorphic part.
              The real problem, as presented by others, is that if you ever add
              anything in a derived class that needs to be destroyed, deleting the
              object through a base-class pointer will most probably not destroy the
              data in the derived class.
              The real problem is that even if he never adds anything, it is
              undefined behavior.
              (Technically speaking deleting through a base class pointer without a
              virtual destructor is undefined behavior even if the derived class is
              empty, but I suppose most compilers will do what you expect.)
              Most don't. (I'd expect an immediate program crash, but with
              most, it will work most of the time, only crashing in specific
              cases, or much later.)

              [...]
              class base
              {
              public:
              ~base()
              {
              delete[] p;
              };
              protected:
              int *p;
              base():p(new int[10])
              {
              };
              base(int *pp) : p(pp)
              {
              };
              };
              You probably shortened the example for the sake of brevity, but a few
              comments nevertheless:
              - Allocating unmanaged memory in the initialization list of the
              constructor is asking for trouble.
              Why? In this case, he's doing it in the approved fashion:
              passing the pointer immediately to the base class or member.
              - If objects of this class are ever copied or assigned, problems will
              happen.
              - Deleting a pointer given to the constructor is dubious practice at
              best. You can't know what it's pointing to.
              That depends on the contract. Is boost::shared_p tr dubious
              practice? It certainly deletes a pointer given to the
              constructor.

              --
              James Kanze (GABI Software) email:james.kan ze@gmail.com
              Conseils en informatique orientée objet/
              Beratung in objektorientier ter Datenverarbeitu ng
              9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

              Comment

              • James Kanze

                #8
                Re: Polymorphism without virtual in C++

                On Aug 7, 3:51 am, feel <feel...@gmail. comwrote:
                My code is a bad example ~_^. But if you can check with AcGe classes
                in ObjectARX(from AutoDesk's AutoCAD sdk), you will see the same
                design. The special case is:
                1 for geometry class, esp for leaf classes, we donot want user to
                extend it. if you want to add other kind of geometry type, please add
                new class.The leaf of class tree is 'sealed' class.
                2 Polymorphism in this design is the Impl pointer in root class. we
                can implementation the real version class for every leaf class.
                3 Except for leaf class, every class can not be initialize in the
                stack.in my example code, you can not do this:
                base1 b;
                Why? because I donot want a big class in my header file. For geometry
                classes,such as AcGeNurbCurve, there are many method we have to put
                into interface. but we can split all these method into different
                catalog: 3dentity, curve, spline curve. Then you will see a class
                tree. There are small number methods in every node(classes in the
                tree). And it's meaningfull for a geometry class library. we may do
                something for all kind of curves,right? so if we use Pimpl idiom, you
                can find Polymorphism.
                This sounds more like letter-envelope than like compilation
                firewall. But I'm not sure I've fully understood it. (The
                letter-envelope idiom is used to make a class with value
                semantics behave polymorphically .)

                --
                James Kanze (GABI Software) email:james.kan ze@gmail.com
                Conseils en informatique orientée objet/
                Beratung in objektorientier ter Datenverarbeitu ng
                9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                Comment

                • feel

                  #9
                  Re: Polymorphism without virtual in C++

                  On Aug 7, 4:55 pm, James Kanze <james.ka...@gm ail.comwrote:
                  On Aug 7, 3:51 am, feel <feel...@gmail. comwrote:
                  >
                  My code is a bad example ~_^. But if you can check with AcGe classes
                  in ObjectARX(from AutoDesk's AutoCAD sdk), you will see the same
                  design. The special case is:
                  1 for geometry class, esp for leaf classes, we donot want user to
                  extend it. if you want to add other kind of geometry type, please add
                  new class.The leaf of class tree is 'sealed' class.
                  2 Polymorphism in this design is the Impl pointer in root class. we
                  can implementation the real version class for every leaf class.
                  3 Except for leaf class, every class can not be initialize in the
                  stack.in my example code, you can not do this:
                  base1 b;
                  Why? because I donot want a big class in my header file. For geometry
                  classes,such as AcGeNurbCurve, there are many method we have to put
                  into interface. but we can split all these method into different
                  catalog: 3dentity, curve, spline curve. Then you will see a class
                  tree. There are small number methods in every node(classes in the
                  tree). And it's meaningfull for a geometry class library. we may do
                  something for all kind of curves,right? so if we use Pimpl idiom, you
                  can find Polymorphism.
                  >
                  This sounds more like letter-envelope than like compilation
                  firewall.  But I'm not sure I've fully understood it.  (The
                  letter-envelope idiom is used to make a class with value
                  semantics behave polymorphically .)
                  >
                  --
                  James Kanze (GABI Software)             email:james.ka. ..@gmail.com
                  Conseils en informatique orientée objet/
                                     Beratung in objektorientier ter Datenverarbeitu ng
                  9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
                  Yes, it's just like that. But the difference is:we have to work on
                  many kind of Envelops.
                  In classic letter-envelop, we can hide the real data deifinition. but
                  if we have to work
                  on a class heirarchy, for example:


                  Entity
                  ^
                  |
                  Curve
                  ^
                  |
                  Line

                  In this case, the real data is in Line, but we have to catalog all the
                  line's method into Entity, curve.
                  Then we can work on all kind of entity or curve.In a summary, we have
                  two requirement:
                  1 data hide, different data implementation.
                  2 class heirarchy.

                  Any comments about these requirements are welcome!

                  Comment

                  • Juha Nieminen

                    #10
                    Re: Polymorphism without virtual in C++

                    feel wrote:
                    2 Polymorphism in this design is the Impl pointer in root class. we
                    can implementation the real version class for every leaf class.
                    Your base class actually looks simply like a (well, some kind of)
                    smart pointer.

                    Yet I still fail to see where the polymorphism is, unless your base
                    class smart pointer is pointing to different class type, which can be
                    derived and have virtual functions. If it doesn't, then it's simply a
                    smart pointer which points to some numeric data, and that's it.

                    I don't believe the definition of "polymorphi sm" is "each object can
                    have differing data". If that was the case, then std::string would be
                    polymorphic because different instances of std::string can have
                    differing data (eg. different string length and contents).

                    In fact, I think std::string is a good comparison point. What are the
                    relevant differences between your classes and std::string (other than
                    std::string doesn't expose its private data in its protected section)?
                    Why? because I donot want a big class in my header file.
                    Is it simply a question of style? You don't want all the private data
                    of the class to be viewable from the header file?

                    Comment

                    • Chris M. Thomasson

                      #11
                      Re: Polymorphism without virtual in C++

                      "feel" <feelapi@gmail. comwrote in message
                      news:35702afc-3c74-4ab2-abea-f1ea08a8da78@t1 g2000pra.google groups.com...
                      On 8月8日, 下午4时17分 , James Kanze <james.ka...@gm ail.comwrote:
                      [...]
                      Thank you for your reply. I think you are right. I do not use virtual
                      functions as interface
                      because the size of object. Saying line, we can put two points as data
                      implementation. But if
                      put many virtual functinos into class, we have to get a bigger object
                      than that object should be:
                      only two points!Object's size is very important in geometry class
                      design.
                      Pimpl can make sure the object's size is suitable for this
                      requirements.
                      Polymorphism means in this case is like this: we can call curve's
                      getLength() function to get curve's
                      length. but for different kind of curves, we have to implementation
                      different method to compute it.
                      Some implementations can have a huge vtable, but the size of the object is
                      going to be a pointer to the vtable:

                      class Interface {
                      virtual void func1() = 0;
                      virtual void func2() = 0;
                      virtual void func3() = 0;
                      virtual void func4() = 0;
                      virtual void func5() = 0;
                      virtual void func6() = 0;
                      virtual void func7() = 0;
                      virtual void func8() = 0;
                      [on and on...];
                      virtual ~Interface() = 0;
                      };

                      Interface::~Int erface() {}


                      could end of resulting in:


                      sizeof(Interfac e) == sizeof(void*);


                      on some impls...

                      Comment

                      • feel

                        #12
                        Re: Polymorphism without virtual in C++

                        On 8ÔÂ9ÈÕ, ÉÏÎç11ʱ12·Ö, "Chris M. Thomasson" <n...@spam.inva lidwrote:
                        "feel" <feel...@gmail. comwrote in message
                        >
                        news:35702afc-3c74-4ab2-abea-f1ea08a8da78@t1 g2000pra.google groups.com...
                        On 8ÔÂ8ÈÕ, ÏÂÎç4ʱ17·Ö, James Kanze <james.ka...@gm ail.comwrote:
                        [...]
                        >
                        Thank you for your reply. I think you are right. I do not use virtual
                        functions as interface
                        because the size of object. Saying line, we can put two points as data
                        implementation. But if
                        put many virtual functinos into class, we have to get a bigger object
                        than that object should be:
                        only two points!Object's size is very important in geometry class
                        design.
                        Pimpl can make sure the object's size is suitable for this
                        requirements.
                        Polymorphism means in this case is like this: we can call curve's
                        getLength() function to get curve's
                        length. but for different kind of curves, we have to implementation
                        different method to compute it.
                        >
                        Some implementations can have a huge vtable, but the size of the object is
                        going to be a pointer to the vtable:
                        >
                        class Interface {
                        virtual void func1() = 0;
                        virtual void func2() = 0;
                        virtual void func3() = 0;
                        virtual void func4() = 0;
                        virtual void func5() = 0;
                        virtual void func6() = 0;
                        virtual void func7() = 0;
                        virtual void func8() = 0;
                        [on and on...];
                        virtual ~Interface() = 0;
                        >
                        };
                        >
                        Interface::~Int erface() {}
                        >
                        could end of resulting in:
                        >
                        sizeof(Interfac e) == sizeof(void*);
                        >
                        on some impls...
                        En, right.But the size is still a problem. for 1000 lines, we can use
                        small memory if we do not use virtual functions.

                        Comment

                        • James Kanze

                          #13
                          Re: Polymorphism without virtual in C++

                          On Aug 9, 4:46 am, feel <feel...@gmail. comwrote:
                          On 8月8日, 下午4时17分 , James Kanze <james.ka...@gm ail.comwrote:
                          Thank you for your reply. I think you are right. I do not use
                          virtual functions as interface because the size of object.
                          Saying line, we can put two points as data implementation. But
                          if put many virtual functinos into class, we have to get a
                          bigger object than that object should be:
                          Only the first virtual function adds to the size of the object;
                          once the object has at least one virtual function, additional
                          virtual functions are free (at least in all of the
                          implementations I've every seen or heard of). Increased size is
                          not a reason to avoid virtual functions.
                          only two points!Object's size is very important in geometry
                          class design.
                          Pimpl can make sure the object's size is suitable for this
                          requirements.
                          The compilation firewall idiom actually increases memory use.
                          Either there's something I don't understand here, or you've
                          misunderstood something.
                          Polymorphism means in this case is like this: we can call
                          curve's getLength() function to get curve's length. but for
                          different kind of curves, we have to implementation different
                          method to compute it.
                          Exactly.

                          --
                          James Kanze (GABI Software) email:james.kan ze@gmail.com
                          Conseils en informatique orientée objet/
                          Beratung in objektorientier ter Datenverarbeitu ng
                          9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                          Comment

                          Working...