2016-07-14 5 views
1

Я пытаюсь создать класс List, используя концепцию связанных списков, и, хотя я изначально использовал ключевое слово C++ standard new, я решил отключить его для C++ 11 std::shared_ptr , Тем не менее, я не могу заставить программу функционировать должным образом при использовании интеллектуальных указателей, так как она сбой. Вот некоторые фрагменты кода до изменения:C++ Устанавливающий указатель Equal to shared_ptr

class List 
{ 
public: 
    void push_back(...) { 
     Node *temp = new Node; 
     ... 
     if (!head) { 
      head = temp; 
      return; 
     } 
     else { 
      Node *last = head; 
      ... 
      last->next = temp; 
     } 
    } 
    ... 

private: 
    Node *head = nullptr; 
}; 

А вот как это выглядит с изменением:

class List 
{ 
public: 
    void push_back(...) { 
     std::shared_ptr<Node> temp(new Node); 
     ... 
     if (!head) { 
      head = temp.get(); 
      return; 
     } 
     else { 
      Node *last = head; 
      ... 
      last->next = temp.get(); 
     } 
    } 
    ... 

private: 
    Node *head = nullptr; // don't need this to be a smart ptr 
}; 

Я чувствую, что проблема может быть, что head и last не выделяется динамически и, возможно, им нужно работать с shared_ptr, но я не уверен. Что именно я делаю неправильно и как я могу это исправить? Я действительно надеюсь, что это не дубликат, потому что я не могу найти ничего, что решает мою проблему. Благодарю.

Edit: Вот Node структура:

struct Node{ 
    int data; 
    Node* next; 
}; 
+0

На первый взгляд, я думаю, проблема, вероятно, в определении 'Node' – Assimilater

+0

Ну, похоже, мне нужен динамический узел' temp', иначе программа не работает, и я был просто учитывая динамическое распределение его с помощью 'shared_ptr', а не стандартного' нового'. И я могу опубликовать структуру «Node», если это поможет. –

+0

да, сообщение 'Node' struct :) – Assimilater

ответ

1

Ваша главная проблема в том, что если вы собираетесь использовать shared_ptr, лучше использовать его всю дорогу. Сделайте next a shared_ptr вместо необработанного.

struct Node { 
    int data; 
    std::shared_ptr<Node> next; 
} 

Что std::shared_ptr делает под капотом держать счета, сколько ссылок есть на указатель. Когда вы используете конструкторы копирования или operator=, он увеличивает счетчик ссылок. Когда экземпляр выпадает из области видимости, приводящей к вызову деструктора (или вы даете ему другой указатель с operator=), декремент счетчика ссылок. Когда счетчик равен нулю, указатель уничтожается.

// pass by value invokes copy constructor (refcount + 1) 
void myFunc(std::shared_ptr<MyClass> var) { 

    // Code using var 

} // end of function invokes destructor (refcount - 1) 

void run() { 
    std::shared_ptr<MyClass> ptr(new MyClass); // refcount = 1 
    myFunc(ptr); // refcount = 2 
    // After myFunc returns refcount = 1 

} 
int main() { 
    run(); // refcount = 1 
    // After run returns, refcount = 0 and the pointer is deleted 
} 

С помощью get() вы приведете указатель на область памяти, которая может быть удалена в какой-то момент, независимо от того, является ли этот указатель вокруг. Это может привести к segfaults, поскольку необработанные указатели указывают на память, которую удалил shared_ptr.

Это потому, что get() не влияет на количество ссылок. Как это могло быть? Это не shared_ptr, так что определение класса не имеет никакого представления о том, что вы с ним делаете, или когда оно удаляется. Если get() увеличил счетчик ссылок, не было бы ничего, чтобы впоследствии его уменьшить, и память никогда не будет выпущена. Это утечка памяти!

+0

Ах, я вижу. Я заменил все вхождения указателей на интеллектуальные указатели (не обязательно динамически распределенные как ваши), и что он исправил проблему. Благодарю. –

+0

На самом деле, вызванный этим вопросом, я узнал сегодня, что они также могут быть реализованы как связанный список * сами * (нужно любить иронию). Смотрите: http://stackoverflow.com/questions/725142/how-does-a-reference-counting-smart-pointers-reference-counting-work – Assimilater

+0

Эй, это очень хорошо! –

4

Причина иметь std::shared_ptr в первую очередь иметь std::shared_ptr взять на себя полную и полную собственность указателя, и сделать его std::shared_ptr «S ответственность delete это после того, как последняя ссылка на указатель уходит. Вот что такое std::shared_ptr.

Это означает, что после того, как указатель помещается в std::shared_ptr, std::shared_ptr теперь берет на себя полную и полную ответственность за управление указателем. Он владеет им полностью.

Это не имеет смысла вставлять указатель в std::shared_ptr ...а затем сразу же принять его:

head = temp.get(); 

Есть причины для функции get() существовать, но это не один из них.

Для правильного использования std::shared_ptr все должно быть std::shared_ptr. head должен быть std::shared_ptr:

std::shared_ptr<Node> head; // yes, it does need to be a smart ptr 

Почему нужно быть std::shared_ptr? Ну, если это не так, как вы думаете, что произойдет, когда это:

std::shared_ptr<Node> temp(new Node); 

В частности, когда это temp смарт-указатель разрушается, когда эта функция возвращает? Ну, так как это будет последний std::shared_ptr, который ссылается на это Node, он будет счастливо delete. Дело в том, что вы get() раньше, и разместили его в head не имеет значения. Итак, теперь у вас есть head, который указывает на delete d Узел. Наступает веселье.

И вот почему все должно быть std::shared_ptr. Не только head, но и Node «S next член также потребности быть std::shared_ptr тоже.

Теперь есть ошибка, связанная с круговыми ссылками, которая входит в игру, когда std::shared_ptr входит в изображение. Но это будет другой вопрос.

+0

Спасибо за отличный подробный ответ. Я решил принять ответ Ассимилатера как его пришедший кулак и решил проблему, но мне очень нравится ваш ответ :) –

+0

@ArchieGertsman Я редактировал свой ответ с такой информацией, как этот ответ пришел :). Это может помочь вам понять немного больше того, что происходит под капотом – Assimilater