1

I have this C++ code:

#include <vector>
using namespace std;

struct Test {
    int* member = 0;

    Test() {}
    Test(const Test& o) { member = new int(*o.member); }
    ~Test() { delete member; }
};

int main()
{
    vector<Test> vecTest = {
        Test(), Test(), Test()
    };

    vecTest.erase(vecTest.begin());
}

This program builds properly, but it crashes with exit code -1073741819. What am I doing wrong?

FireFragment
  • 596
  • 1
  • 4
  • 15

1 Answers1

3

-1073741819 is hex 0xC0000005. That is the exit code for an uncaught Access Violation exception. Which means your code is accessing invalid memory.

Your vector is initialized with Test objects that are holding null pointers. When you erase the 1st object, the remaining objects have to be moved down in the vector. But, since your class does not implement move semantics, the objects have to be copied instead. Your copy constructor is not handling the case when o.member is nullptr. And your class does not implement a copy assignment operator at all, thus violating the Rule of 3/5/0, which can lead to memory leaks and multiple objects ending up holding the same pointer causing double-deletes of memory.

Try this instead:

class Test {
    int* member;
public:
    Test() { member = new int(); }
    Test(const Test& o) { member = new int(*(o.member)); }
    ~Test() { delete member; }

    Test& operator=(const Test& rhs) {
        if (&rhs != this) {
            *member = *(rhs.member);
        }
        return *this;
    }
};

Or, with move semantics added:

class Test {
    int* member;
public:
    Test() { member = new int(); }
    Test(const Test& o) { member = new int(*(o.member)); }
    Test(Test&& o) { member = o.member; o.member = nullptr; }
    ~Test() { delete member; }

    Test& operator=(Test rhs) {
        std::swap(member, rhs.member);
        return *this;
    }
};

That being said, consider using std::unique_ptr to help with memory management:

class Test {
    std::unique_ptr<int> member;
public:
    Test() { member = std::make_unique<int>(); }
    Test(const Test& o) { member = std::make_unique<int>(*(o.member)); }
    Test(Test&& o) { member = std::move(o.member); }

    Test& operator=(Test rhs) {
        std::swap(member, rhs.member);
        return *this;
    }
};
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • maybe worth mentioning that its not the kind of exception one can `catch` – 463035818_is_not_an_ai May 30 '21 at 19:21
  • @463035818_is_not_a_number yes, they can, via `try..catch` – Remy Lebeau May 30 '21 at 19:22
  • then I am missing something, tried it here: https://godbolt.org/z/YEz14Gj5n – 463035818_is_not_an_ai May 30 '21 at 19:26
  • @463035818_is_not_a_number it is compiler-specific. Some compilers can catch OS errors with `catch`. Some can't – Remy Lebeau May 30 '21 at 19:39
  • @RemyLebeau, Thanks for a great answer. I'd like to have 1 question. You wrote that the OP's copy constructor is not handling the case when o.member is nullptr. I wonder if your copy constructor handles that case ? I can see some differences between these 2 copy constructors but I am not 100% sure how that case is handled in your copy constructor. Thanks. – Job_September_2020 May 30 '21 at 19:54
  • 1
    @Job_September_2020 if you look more carefully, I had updated the default constructor to ensure `member` is never `nullptr` to begin with. – Remy Lebeau May 30 '21 at 21:18