Понимание умных указателей, но ошибка: освобожденный указатель не был выделен - PullRequest
0 голосов
/ 27 октября 2018

Я пытаюсь понять умные указатели и имею следующий код:

 #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;
 }

Здесь я пытаюсь получить доступ к необработанному указателю, но через интеллектуальный указатель. Он печатает «hello world» просто отлично, и нет никаких ошибок, когда я закомментирую деструктор для A. Однако, когда я раскомментирую его, я получаю следующую ошибку:

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

Что здесь происходит? Вызывает ли shared_ptr, p деструктор, когда он выходит из области видимости или сбрасывается (в nullptr)? Как p справляется с утечкой памяти от не удаления A :: a?

Я понимаю, что умные указатели обычно обрабатывают новые объекты, и это, вероятно, случай, который не часто используется, но я хочу попытаться изучить это.

Ответы [ 3 ]

0 голосов
/ 27 октября 2018

Ваш p.reset () удаляет объект B, так как он ничего не знает о переменной a, владеющей им, и вы не увеличивали счетчик ссылок p вручную (что невозможно с shared_ptr, поскольку он предназначен только для совместного использованияуказатель путем копирования объекта shared_ptr).

То, что вы пытались сделать здесь с помощью reset (ptr), является неопределенным поведением:

Если объект, на который указывает ptr, уже принадлежит,функция приводит к неопределенному поведению.

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

0 голосов
/ 27 октября 2018

Является ли shared_ptr, p вызывающим деструктором, когда он выходит из области видимости или сбрасывается (в nullptr)?

Да, точно.

Как p справляется с утечкой памяти из-за того, что A :: a?

p не думает, что ему принадлежит объект, поэтому он удаляет его.Там нет утечки.Но a также думает, что ему тоже принадлежит объект, поэтому он пытается удалить его во второй раз и вылетает.

Я понимаю, что умные указатели обычно обрабатывают новые объекты, и это, вероятно, случай, который не часто используется, но я хочу попытаться изучить это.

Это случайне используется вообще, потому что вы не можете иметь несколько объектов, владеющих одной и той же памятью одновременно, именно по этой причине.

Правильным решением является либо class A использовать shared_ptr внутри:

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;
}

Или просто не берите в собственность необработанный указатель, для которого вы сами не назначали:

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

Весь смысл использования умных указателей в том, чтобы быть явно о том, кто чем владеет и как передается право собственности.Ваш пример нарушает это.

0 голосов
/ 27 октября 2018

A a; и shared_ptr<B> p(a.get()); указывают на один и тот же выделенный B объект в памяти.p.reset() уничтожает этот B объект, а затем деструктор ~A() пытается снова его освободить.

С shared_ptr::reset():

Если*this уже владеет объектом, и он является последним shared_ptr владельцем, объект уничтожен через принадлежащий deleter.

...