Strange behavoiur of a virtual destructor

Discussion in 'C++' started by Gutek, Apr 23, 2005.

  1. Gutek

    Gutek Guest

    Hello all,

    I have run into the following problem with virtual destructor. I have
    following classes:

    class A
    {
    public:
    double* p;
    A(unsigned int s=0) : { p = new double ; }
    virtual ~A() { delete [] p; }
    };

    class B : public A
    {
    public:
    unsigned int val;
    B(unsigned int v=0) : A(v), val(v) { }
    B& operator=(const B& b);
    ~B() { }
    };

    B& B::eek:perator=(const B& b)
    {
    this->~B();
    val = b.val;
    p = new double[val];
    copy(b.p, b.p+val,p);
    return *this;
    }


    Now I create a dynamic array of objects B:

    B* bp = new B[4];
    B b(7);
    bp[0] = b;
    delete [] bp;

    Now at the last line the following run-time error occurs:

    Debug Assertion Failed!:
    Expression: _CrtIsValidHeapPointer(pUserData)

    I have checked that only virtual destructor ~A() is called. The strange
    thing is that when destructor ~A() is not virtual or b is assigned to
    some other cell, e.g. bp[1] = b;, the problem disappears.

    Do you know what is the reson of this?

    Regards,

    Gutek
    Gutek, Apr 23, 2005
    #1
    1. Advertising

  2. Gutek

    Pete Becker Guest

    Gutek wrote:
    >
    > B& B::eek:perator=(const B& b)
    > {
    > this->~B();


    The object has just been destroyed. Anything done to it after this is,
    at best, questionable.

    > val = b.val;
    > p = new double[val];
    > copy(b.p, b.p+val,p);
    > return *this;
    > }


    B& B::eek:perator=(const B& b)
    {
    if (this != &b)
    {
    double *tmp = new double[b.val];
    delete [] p;
    p = tmp;
    copy(b.p, b.p + val, p);
    val = b.val;
    }
    return *this;
    }

    The order of the new and delete is important: if new throws an exception
    the assignment operator leaves *this unchanged.

    --

    Pete Becker
    Dinkumware, Ltd. (http://www.dinkumware.com)
    Pete Becker, Apr 23, 2005
    #2
    1. Advertising

  3. Gutek

    Gutek Guest

    Pete Becker napisał(a):
    >
    >
    > B& B::eek:perator=(const B& b)
    > {
    > if (this != &b)
    > {
    > double *tmp = new double[b.val];
    > delete [] p;
    > p = tmp;
    > copy(b.p, b.p + val, p);
    > val = b.val;
    > }
    > return *this;
    > }
    >
    > The order of the new and delete is important: if new throws an exception
    > the assignment operator leaves *this unchanged.
    >


    Thanks, this solves this problem. But what if I need to call the
    destructor of A anyway? It may do sth more that just 'delete [] p;', for
    example sth on private members of A. When writing my own operator=() I
    used to call the destructor instead of do the clean up myself, and then
    alloate memory and copy. Can I do sth with the following:

    B& B::eek:perator=(const B& b)
    {
    if (this != &b)
    {
    double *tmp = new double[b.val];
    //delete [] p;
    this->~B();
    p = tmp;
    copy(b.p, b.p + val, p);
    val = b.val;
    }
    return *this;
    }

    Regard,

    Gutek
    Gutek, Apr 23, 2005
    #3
  4. Gutek

    Rolf Magnus Guest

    Gutek wrote:

    > Pete Becker napisał(a):
    >>
    >>
    >> B& B::eek:perator=(const B& b)
    >> {
    >> if (this != &b)
    >> {
    >> double *tmp = new double[b.val];
    >> delete [] p;
    >> p = tmp;
    >> copy(b.p, b.p + val, p);
    >> val = b.val;
    >> }
    >> return *this;
    >> }
    >>
    >> The order of the new and delete is important: if new throws an exception
    >> the assignment operator leaves *this unchanged.
    >>

    >
    > Thanks, this solves this problem. But what if I need to call the
    > destructor of A anyway?


    Then you should ask yourself why you need that. It's usually needed very
    rarely if at all. It's easy to do 10 years of C++ programming 8 hours per
    day and never ever need to use an explicit destructor call.

    > It may do sth more that just 'delete [] p;', for example sth on private
    > members of A.


    Why doesn't A have a proper assignment operator then?
    BTW: IMHO, assignment operators on polymorphic types are only of limited
    use.

    > When writing my own operator=() I used to call the destructor instead of
    > do the clean up myself, and then alloate memory and copy. Can I do sth
    > with the following:
    >
    > B& B::eek:perator=(const B& b)
    > {
    > if (this != &b)
    > {
    > double *tmp = new double[b.val];
    > //delete [] p;
    > this->~B();


    As someone already told you, the object is destroyed here. There is no
    object anymore, so you must not assign its members or call any member
    functions of it. On some systems, you might get away with it, but it's not
    guaranteed to work. it's bad programming practice and it invokes undefined
    behavior. It's one of those "don't do this at home" things.

    > p = tmp;
    > copy(b.p, b.p + val, p);
    > val = b.val;
    > }
    > return *this;
    > }
    >
    > Regard,
    >
    > Gutek
    Rolf Magnus, Apr 23, 2005
    #4
  5. Gutek

    Gutek Guest

    Rolf Magnus napisał(a):

    >
    > As someone already told you, the object is destroyed here. There is no
    > object anymore, so you must not assign its members or call any member
    > functions of it. On some systems, you might get away with it, but it's not
    > guaranteed to work. it's bad programming practice and it invokes undefined
    > behavior. It's one of those "don't do this at home" things.
    >


    Ok. Thank you Pete and Rolf. In fact, this was not my invention to do
    so, but I've read somewhere, that destructors do the clean-up but do not
    destroy the object, in similar way like constructors initialize the
    object but do not create it. Seems that I was misleaded.

    Kind regards,

    Gutek
    Gutek, Apr 23, 2005
    #5
  6. Gutek

    Pete Becker Guest

    Gutek wrote:

    >
    > Thanks, this solves this problem. But what if I need to call the
    > destructor of A anyway? It may do sth more that just 'delete [] p;', for
    > example sth on private members of A.


    Don't call the destructor. Write an assignment operator for A, and call
    that. (Yes, I know, you can't do that with this particular version of A;
    that's a design flaw in A.) Or add a member function to A that cleans it
    up, and call that from A's destructor and call it from B's assignment
    operator.

    > When writing my own operator=() I
    > used to call the destructor instead of do the clean up myself, and then
    > alloate memory and copy. Can I do sth with the following:
    >
    > B& B::eek:perator=(const B& b)
    > {
    > if (this != &b)
    > {
    > double *tmp = new double[b.val];
    > //delete [] p;
    > this->~B();


    No. You've destroyed the object. There's nothing sensible you can do
    with that object any more, because it does not exist.

    --

    Pete Becker
    Dinkumware, Ltd. (http://www.dinkumware.com)
    Pete Becker, Apr 23, 2005
    #6
  7. Gutek

    Pete Becker Guest

    Gutek wrote:

    >
    > Ok. Thank you Pete and Rolf. In fact, this was not my invention to do
    > so, but I've read somewhere, that destructors do the clean-up but do not
    > destroy the object, in similar way like constructors initialize the
    > object but do not create it. Seems that I was misleaded.
    >


    Constructors construct and destructors destroy. They don't allocate
    memory or free it, which is probably what someone was trying to say.

    --

    Pete Becker
    Dinkumware, Ltd. (http://www.dinkumware.com)
    Pete Becker, Apr 23, 2005
    #7
  8. Gutek

    Gutek Guest

    Pete Becker napisał(a):
    >
    > Don't call the destructor. Write an assignment operator for A, and call
    > that. (Yes, I know, you can't do that with this particular version of A;
    > that's a design flaw in A.)


    You mean:

    A& A::eek:perator=(const A& a)
    {
    delete [] p;
    p = new double[a.size];
    size = a.size;
    copy(a.p, a.p+size,p);
    return *this;
    }

    B& B::eek:perator=(const B& b)
    {
    A::eek:perator=(b);
    val = b.val;
    return *this;
    }

    ?

    > No. You've destroyed the object. There's nothing sensible you can do
    > with that object any more, because it does not exist.


    It looks like 'destroy' means something more than 'call the code from
    the destructor' but still less that 'free the memory of the object'. So
    what it is exactly?

    Gutek
    Gutek, Apr 23, 2005
    #8
  9. Gutek

    Stephen Howe Guest

    "Gutek" <beztego_g_itego_utkowski@vp_itego.pl> wrote in message
    news:d4dejb$fa3$...

    I have just had a look at the code presented. It seems flawed.

    (i) Because of A's internal pointer, you should write a copy constructor and
    assignment operator to handle all this. Failure to do this will eventually
    burn you.

    (ii) For B, you don't need to bother writing an assignment operator, you can
    strip it out. If A's constructor, copy constructor, assignment operator and
    destructor all "work", then the automatic compiler-generated versions for B
    will call A's equivalents. B, as a class, unlike A, has nothing that needs
    manually handling, therefore you don't need to write an assignment operator.

    This is the least amount of work.

    Stephen Howe
    Stephen Howe, Apr 23, 2005
    #9
  10. Gutek

    Pete Becker Guest

    Gutek wrote:
    >
    > You mean:
    >
    > A& A::eek:perator=(const A& a)
    > {
    > delete [] p;
    > p = new double[a.size];
    > size = a.size;
    > copy(a.p, a.p+size,p);
    > return *this;
    > }
    >
    > B& B::eek:perator=(const B& b)
    > {
    > A::eek:perator=(b);
    > val = b.val;
    > return *this;
    > }
    >
    > ?


    Yes. That's the usual way: each class is responsible for its own copy
    operation.

    >
    >> No. You've destroyed the object. There's nothing sensible you can do
    >> with that object any more, because it does not exist.

    >
    >
    > It looks like 'destroy' means something more than 'call the code from
    > the destructor' but still less that 'free the memory of the object'. So
    > what it is exactly?
    >


    I've been deliberately not saying what it means, because it depends on
    various implementation details. So read this, but forget it. <g>
    Typically, a class with virtual functions has a vtable that holds
    pointers to the virtual functions. So an A object has a pointer to A's
    vtable, and a B object has a pointer to B's vtable. When you destroy a B
    object the destructor does whatever code you've written, then replaces
    the vtable pointer with a pointer to A's vtable, then calls A's
    destructor. The result is memory with none of the proper internal
    details to act as an A object or as a B object.

    --

    Pete Becker
    Dinkumware, Ltd. (http://www.dinkumware.com)
    Pete Becker, Apr 23, 2005
    #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. Jim Higson

    How to fix odd IE behavoiur?

    Jim Higson, Sep 13, 2004, in forum: HTML
    Replies:
    1
    Views:
    350
    Jim Higson
    Sep 13, 2004
  2. Calvin Lai
    Replies:
    7
    Views:
    538
    Calvin Lai
    Dec 18, 2003
  3. Chunhui Han
    Replies:
    2
    Views:
    487
  4. Heinrich Mislik

    Strange behavoiur when passing $1 to a sub

    Heinrich Mislik, Oct 12, 2009, in forum: Perl Misc
    Replies:
    5
    Views:
    98
    Ilya Zakharevich
    Oct 17, 2009
  5. Peter Otten

    Re: odd behavoiur seen

    Peter Otten, Jul 22, 2013, in forum: Python
    Replies:
    0
    Views:
    72
    Peter Otten
    Jul 22, 2013
Loading...

Share This Page