Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double free of child object after using the copy constructor

I am having trouble figuring out why (it seems like) an object is being destructed twice.

If i create a object of a class (B) which contains an object of another class (A) and i copy this object. the copied object is destructed twice. Altough it looks like this. I am unable to figure out this output.

I have created the following (minimum?) example which seems to trigger my issue:

#include <stdio.h>
#include <stdint.h>

template <class T>
class A
{
public:
    A()
    {
        myCtr = ++ctr;
        printf("class A default Constructor - object id: %u\n", myCtr);
    }

    A(const A<T> &a2) {
        myCtr = ++ctr;
        printf("class A copy constructor - object id: %u\n", myCtr);

    }

    ~A()
    {
        printf("class A destructor - object id: %u\n", myCtr);
    }

    void add(T item) {
        /* Irrelevant */
    }

private:
    uint64_t myCtr;
    static uint64_t ctr;
};

class B
{
public:
    B() {

    }

    B(char * input, uint32_t len) {
        for (uint32_t i = 0; i < len; i++)
        {
            characters.add(input[i]);
        }
    }

    B(const B &b2) {
        characters = A<char>(b2.characters);
    }

    ~B() {

    }


private:
    A<char> characters;
};

template <class T>
uint64_t A<T>::ctr = 0;

int main(int argc, char *argv[]) {
    B b1 = B((char *)"b1", 2);
    B b2 = B(b1);

    return 0;
}

This produces the following output:

class A default Constructor - object id: 1
class A default Constructor - object id: 2
class A copy constructor - object id: 3
class A destructor - object id: 3
class A destructor - object id: 3
class A destructor - object id: 1

object id 3 is destructed twice while object id 2 is not destructed at all.

I am using the following compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.14.26429.4

In case you down vote. Please specify why. I will gladly try to improve my question.

like image 596
eKKiM Avatar asked Jan 28 '23 23:01

eKKiM


2 Answers

The confusion comes from this line.

characters = A<char>(b2.characters);

A default generated assignment operator copies object 3 to object 2, including myCtr.

myCtr is not const, so it may change during its lifetime. If you need a value to remain the same, make it const.

Here is a version of your class that will behave the way you expect.

template <class T>
class A
{
public:
    A()
        : myCtr( ++ctr )
    {
        printf("class A default Constructor - object id: %u\n", myCtr);
    }

    A(const A<T> &a2)
        : myCtr( ++ctr )
    {
        printf("class A copy constructor - object id: %u\n", myCtr);

    }

    A<T>& operator=(const A<T> &a2) {
        // Copy what's needed from a2 here.
        return *this;
    }

    ~A()
    {
        printf("class A destructor - object id: %u\n", myCtr);
    }

    void add(T item) {
        /* Irrelevant */
    }

private:
    const uint64_t myCtr;
    static uint64_t ctr;
};
like image 27
Drew Dormann Avatar answered Feb 06 '23 15:02

Drew Dormann


You need to follow the rule of 5. If you implement a non-trivial destructor, copy/move assign/construct, you must implement all 5, or give a good reason why not, or delete them.

You implemented destory and copy ctor. You neglected the other 3. Add them.

What more characters = A<char>(b2.characters); your copy ctor calls a copy assignment. Which you don't track, which is why your count is off. You assign over the counter.

like image 107
Yakk - Adam Nevraumont Avatar answered Feb 06 '23 16:02

Yakk - Adam Nevraumont