2016-09-02 6 views
1

Так что прошло некоторое время с тех пор, как я сделал какое-либо C++-кодирование, и мне просто интересно, какие переменные в базовом связанном списке должны быть удалены в деструкторе и, к сожалению, Проконсультируйтесь с моим справочником по C++ в данный момент по этому вопросу. Связанный класс список выглядит следующим образом:Что все должно быть удалено в деструкторе класса

#include <string> 
#include <vector> 

class Node 
{ 
    Node *next; 
    string sName; 
    vector<char> cvStuff; 

    Node(string _s, int _i) 
    { 
     next = nullptr; 
     sName = _s; 

     for (int i = 0; i < _i; i++) 
     { 
      cvStuff.insert(cvStuff.end(), '_'); 
     } 
    } 

    ~Node() 
    { 
     //since sName is assigned during runtime do I delete? 
     //same for cvStuff? 
    } 
}; 

Я тоже любопытно, если в деструкторе я называю

delete next; 

будет, что перейти на следующий узел связанного списка и удалить этот узел и таким образом, рекурсивно удалить весь список из этой точки? Кроме того, если это так, и я по какой-то причине решил это реализовать, должен ли я проверить, является ли следующее nullptr, прежде чем удалять его или это не изменит ситуацию?

спасибо.

+1

Это пытается быть ListNode и List. Кто владеет списком? Вы делитесь хвостами списков, если они одинаковы? Вы планируете удалять элементы или просто весь список? – lorro

+0

@lorro, да, это технически класс узлов и был соответствующим образом скорректирован. Определить хвосты доли? Кроме того, удалите весь список. –

+0

в этом случае, не удаляйте! В противном случае вы не сможете быстро написать стирание, которое удалит любой элемент списка. Совместное использование хвостов пересылаемого списка означает, что, учитывая, что два списка должны иметь одинаковые элементы на обратной стороне, вы разделяете узлы. Очень полезно со списками, которые когда-то были построены и стали const. – lorro

ответ

1

В идеале, ничего: использовать смарт-указатели: std::unique_ptr<>, std::smart_ptr<>, boost::scoped_ptr<> и т.д ..

В противном случае, вы удаляете что владеющий родной указатель. Есть next владеет?

  • Вы планируете удалить что-то посреди списка?Если да, вы не можете удалить в деструкторе.
  • Вы собираетесь поделиться с друзьями? Если да, вам нужны ориентированные на подсчет интеллектуальные указатели.

Это нормально удалить nullptr (ничего не делает). В этом примере вы не должны удалять sName и cvStuff, поскольку они ограничены, поэтому они автоматически уничтожаются.

Кроме того, если это будет список, который может стать большим, вы можете уничтожить & deallocate *next вручную. Это связано с тем, что вы не хотите выходить из пространства стека путем рекурсии.

Кроме того, я предлагаю отделить это от List, что означает структуру данных и ListNode, что означает элемент. Ваши вопросы на самом деле показывают эту двусмысленность, что вы не знаете, удаляете ли вы ListNode или List в деструкторе. Разделение их решает это.

+0

Я не уверен, принадлежит ли вам следующее. 1: думая о функциональности, я никогда не удалю только один элемент, но удалю весь список, поэтому я хотел знать о всей части «рекурсивного деструктора». 2: Я не знаю, что значит делиться хвостами, это когда есть несколько указателей, указывающих на один объект? 3: список никогда не будет больше шести, но в отношении ручного уничтожения из-за истечения пространства стека. Я предполагаю, что вы имеете в виду вручную, как при удалении узлов итеративно в классе, который содержит эти узлы? 4: да, класс следует называть «узлом» –

+0

1. Если вы никогда не захотите стереть, это зависит от вас. Я бы решительно рассмотрел unique_ptr <> даже тогда - таким образом, вам не нужно делать этот выбор. 2. точно - если вы используете общий доступ, используйте shared_ptr <> или sg. аналогично 3. В этом случае забудьте список и сохраните небольшой вектор элемента ptrs (или даже элементов, если сможете). кроме этого, да, я имел в виду итерацию. 4. ok; Тем не менее, я бы предложил иметь «Список», который удаляет, и «Узел», который не является (наименее неожиданным), но выбор открыт. – lorro

+0

Отлично, спасибо за совет =) –

0

В принципе вы должны просто

delete next 

и это все, что вы должны сделать:

  • В string и vector объекты имеют свои собственные деструкторы, и так как этот объект является подорванный, theirs will be called.

  • Deleting a null pointer is not a problem, так что вам даже не нужно проверять это.

  • Если next не является нулевым указателем, он будет продолжать называть деструкторы следующих узлов, в том числе и при необходимости.

Просто удалите его и все.

+1

Хм, это правда только для списков, не содержащих хвост. Для не-const-списков вы не можете удалить следующее: это запретит стирание элемента O (1). Для списков совместного использования хвостов это еще хуже: вы удалите все остальные элементы. – lorro

+1

@lorro Для рекурсивных структур данных вам действительно нужно использовать интеллектуальные указатели подсчета ссылок, такие как 'shared_ptr'; все остальное было бы кошмаром. Структуры данных, разделяющие узлы, намного реже, возможно, вы могли бы упомянуть об этом в следующем вопросе. –

+0

@AmiTavory: lorro не является вопросом афориста –

0

Вызывается деструктор класса (он пуст), вызываются деструкторы объектов-членов.

Если элемент не является объектом, вызывающий деструктор не вызывается.

В вашем примере:

- List *next: pointer on List: no destructor called 
- string sName: string object: destructor called 
- vector<char> cvStuff: vector object: destructor called 

Хорошая новость: у вас нет ничего общего. Объявление деструктора здесь даже не полезно.

Если вы удалите next в своем деструкторе, то удаление элемента приведет к удалению всех других элементов вашего списка: не очень полезно.

(и ваш List объект скорее следует называть Node). Список - это скопированный результат всех ваших узлов, удерживаемый первым созданным вами узлом.

1

Объект с автоматической жизнью имеет это деструктор вызывается, когда он выходит из области видимости:

{ // scope 
    std::string s; 
} // end scope -> s.~string() 

Динамического объект (выделяется новым) не имеет его деструктор не называется, если delete называется на нем.

Для переменной-члена область действия - это время жизни объекта.

struct S { 
    std::string str_; 
    char* p_; 
}; 

int main() { // scope 
    { // scope 
     S s; 
    } // end scope -> s.~S() -> str_.~string() 
} 

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

Итак, в вашем классе списка единственное, о чем вам нужно беспокоиться, это ваш член next: вам нужно решить, является ли он «владеющим» указателем или нет. Если это «владеющий» указатель, вы должны вызвать delete на объект в вашем деструкторе.

В качестве альтернативы, вы можете использовать «RAII» (Приобретение ресурсов является инициализация) и использовать объект обернуть указатель и обеспечить деструктор, который будет вызывать delete для вас:

{ // scope 
    std::unique_ptr<Node> ptr = std::make_unique<Node>(args); 
} // end scope -> ptr.~unique_ptr() -> delete -> ~Node() 

unique_ptr является чисто другой альтернативой может быть shared_ptr, который использует подсчет ref, чтобы базовый объект был только delete d, когда у вас нет оставшихся shared_ptr s к объекту.

Вы бы рассмотреть ваши next указатель быть не владеющим указатель, если, скажем, вы сохранили фактические адреса Node с где-либо еще:

std::vector<Node> nodes; 
populate(nodes); 

list.insert(&nodes[0]); 
list.insert(&nodes[1]); 
// ... 

в вышеуказанном случае вектор имеет узлы и вы определенно не должны называть delete в деструкторе Node, потому что Node s не являются вашими, чтобы удалить.

list.insert(new Node(0)); 
list.insert(new Node(1)); 

здесь, список/Узлов единственные вещи, которые имеют указатели на узлы, так что в этом случае использования нам нужно Node::~Node позвонить удалить или у нас есть утечка.