简体   繁体   中英

Understanding smart pointers but error: pointer being freed was not allocated

I am trying to understand smart pointers and have the following code:

 #include <iostream>
 #include <string>
 #include <memory>
 using namespace std;

 struct B 
 {
     string hello() { return "hello world"; }
 };

 class A
 {
 private:
     B* a;
 public:
     A() { a = new B; }
     ~A() { delete a; a = nullptr; }
     B* get() { return a; }
 };

 int main(int argc, const char * argv[]) {
     A a;
     shared_ptr<B> p(a.get());
     cout << p->hello() << endl;
     p.reset();
     return 0;
 }

What I am trying to do here, is access the raw pointer, but through the smart pointer. It prints "hello world" just fine, and there are no errors when I comment out the destructor for A. However, when I uncomment it, I get the following error:

 test(9758,0x7fff738d9300) malloc: *** error for object 0x1001053a0: pointer 
 being freed was not allocated
 *** set a breakpoint in malloc_error_break to debug

What is going on here? Is the shared_ptr, p, calling the destructor when it goes out of scope or resets (to nullptr)? How is p dealing with the memory leak from not deleting A::a?

I understand that smart pointers generally handle new objects and this is probably a case not used often, but I want to try to learn this.

Is the shared_ptr, p, calling the destructor when it goes out of scope or resets (to nullptr)?

Yes, exactly.

How is p dealing with the memory leak from not deleting A::a?

p thinks it owns the object, so it deletes it. There is no leak. But a also thinks it owns the object too, so it tries to delete it a second time and crashes.

I understand that smart pointers generally handle new objects and this is probably a case not used often, but I want to try to learn this.

This is a case not used at all, because you can't have multiple objects owning the same memory at the same time, for exactly this reason.

The correct solution is to either make class A use shared_ptr internally:

class A {
    private:
        shared_ptr<B> b;
    public:
        A() { b.reset(new B); }
        shared_ptr<B> get() { return b; }
};

int main(int argc, const char * argv[]) {
    A a;
    shared_ptr<B> p = a.get();
    cout << p->hello() << endl;
    p.reset();
    return 0;
}

Or simply don't take ownership of a raw pointer you didn't direct allocate yourself to begin with:

int main(int argc, const char * argv[]) {
    A a;
    B *p = a.get();
    cout << p->hello() << endl;
    return 0;
}

The whole point of using smart pointers is to be explicit about who owns what, and how ownership is transferred around. Your example is violating that.

A a; and shared_ptr<B> p(a.get()); are pointing at the same allocated B object in memory. p.reset() destroys that B object, and then the ~A() destructor tries to free it again.

From shared_ptr::reset() :

If *this already owns an object and it is the last shared_ptr owning it, the object is destroyed through the owned deleter .

Your p.reset() deletes the B object as it doesn't know anything about variable a owning it and you didn't increase p's reference count manually (which is not possible with shared_ptr as it is designed to only share a pointer by copying the shared_ptr object).

What you tried to do here with reset(ptr) is undefined behavior:

If the object pointed to by ptr is already owned, the function results in undefined behavior.

https://en.cppreference.com/w/cpp/memory/shared_ptr/reset

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM