2010-09-25 30 views
8

Niedawno wprowadzono mnie do istnienia auto_ptr i shared_ptr i mam dość proste/naiwne pytanie.Wskaźniki vs auto_ptr vs shared_ptr

Próbuję zaimplementować strukturę danych i muszę wskazać dzieciom z numeru Node, które (mogą mieć więcej niż 1 i jego numer) mogą ulec zmianie. Który jest najlepszym rozwiązaniem i dlaczego:

class Node 
{ 
    public: 
     // ... 
     Node *children; 

    private: 
     //... 
} 

class Node 
{ 
    public: 
     // ... 
     share_ptr<Node> children; 

    private: 
     //... 
} 

Nie jestem pewien, ale myślę auto_ptr nie działa dla tablic. Nie jestem także pewien, czy powinienem używać podwójnych wskaźników. Dzięki za pomoc.

+0

auto_ptr jest przestarzałe w C++ 11 i należy unikać, jeśli to możliwe w kodzie w starszej wersji C++, jak również. – Nikko

+0

'auto_ptr' jest przestarzałe, ponieważ jest niepotrzebnie trudne w użyciu. Zamiast tego użyj 'unique_ptr', który jest w zasadzie taki sam jak' auto_ptr', tylko że działa poprawnie i obsługuje tablice. Jest dostępny od wersji C++ 11. – nwp

+0

również zajrzyj na http://stackoverflow.com/questions/3987521/how-bad-is-to-use-void-pointer-in-stdvector-declaration – fizzbuzz

Odpowiedz

7

Masz rację, że auto_ptr nie działa dla tablic. Kiedy niszczy obiekt, który posiada, używa delete object;, więc jeśli użyjesz new objects[whatever];, otrzymasz niezdefiniowane zachowanie. Być może nieco subtelniej, auto_ptr nie spełnia wymagań "Kopiowania" (jak definiuje to określenie), więc nie można utworzyć kontenera (wektor, deque, lista itp.) Z auto_ptr.

A shared_ptr dotyczy również pojedynczego obiektu. Jest to sytuacja, w której masz współwłasność i musisz usunąć obiekt tylko wtedy, gdy właściciele wykroczą poza zakres. Chyba że coś się dzieje, o czym nam nie powiedziałeś, jest całkiem niezłe, że nie pasuje do twoich wymagań.

Możesz chcieć spojrzeć na jeszcze jedną klasę, która może być dla ciebie nowa: Zwiększ ptr_vector. Przynajmniej na podstawie tego, co powiedziałeś, wydaje się, że pasuje on do Twoich wymagań lepiej niż auto_ptr lub shared_ptr.

+0

Dzięki! Więc rozumiem, że powinienem iść z 'ptr_vector' lub' Node * children'. Nie mogłem użyć 'auto_ptr' do wskazania' std :: vector' 'Node's? Co więcej, czy "Węzeł * dzieci" ma prawo, czy powinienem preferować 'Węzeł ** dzieci'? Jestem trochę zmieszany. Przepraszamy za pakowanie zbyt wielu pytań tutaj. –

+0

@myle: Nie wiedząc więcej o tym, co robisz, trudno powiedzieć na pewno. Zasadniczo 'węzeł *' da ci jeden punkt do dowolnej liczby węzłów, więc te węzły będą w zasadzie częścią ich rodzica, a nie tylko z nim związane. 'Node **' pozwoli ci mieć dynamiczną tablicę wskaźników do węzłów, ale będziesz musiał samodzielnie zarządzać tablicą dynamiczną. –

3

Z powodzeniem użyłem std::vector<std::shared_ptr<Node> > children w podobnej sytuacji.

Główną zaletą korzystania z wektora wspólnych_publik, a nie z tablicy, jest to, że wszystkie zarządzanie zasobami jest obsługiwane. Jest to szczególnie przydatne w dwóch sytuacjach:
1) Gdy wektor nie jest już w zakresie, automatycznie wywołuje usuwanie na całej jego zawartości. W takim przypadku liczba odwołań węzła podrzędnego spadnie o 1 i jeśli nic innego nie będzie się do niego odwoływać, na obiekcie zostanie wywołane usuwanie.
2) Jeśli odwołujesz się do Węzła w innym miejscu, nie ma ryzyka, że ​​pozostanie zwisający wskaźnik do usuniętego obiektu. Obiekt zostanie usunięty tylko wtedy, gdy nie będzie już żadnych odniesień do niego.

Chyba że chcesz zachowania, które jest znacznie bardziej skomplikowane (być może istnieje powód, dla którego tablica jest konieczna), sugeruję, że może to być dla ciebie dobre podejście.

Prosta realizacja idei:

class Node { 
private: 
    T contents; 
    std::vector<std::shared_ptr<Node> > children; 

public: 
    Node(T value) : contents(value) {}; 

    void add_child(T value) { 
     auto p = std::make_shared<Node>(value); 
     children.push_back(p); 
    } 

    std::shared_ptr<Node> get_child(size_t index) { 
     // Returning a shared pointer ensures the node isn't deleted 
     // while it is still in use. 
     return children.at(index); 
    } 

    void remove_child(size_t index) { 
     // The whole branch will be destroyed automatically. 
     // If part of the tree is still needed (eg. for undo), the 
     // shared pointer will ensure it is not destroyed. 
     children.erase(children.begin() + index); 
    } 

}; 
+0

Należy również zauważyć, że można udostępnić własny destruktor do shared_ptr, aby można było użyć jednego do zarządzania tablicą przez wywołanie metody delete [] zamiast zwykłego usunięcia. – QuesterZen

+0

Najprostszym (ale nieco brzydkim) sposobem dodania niestandardowego deletera dla tablic jest użycie funkcji lambda, która jest przekazywana do konstruktora jako dodatkowy argument. Np .: 'std :: shared_ptr sp (nowe T [n], [] (T * p) {delete [] p;})'. Dla unique_ptrs dostępna jest specjalna wersja: 'std :: unique_ptr up (new T [n])', która wywołuje delete [] zamiast delete. – QuesterZen

1

auto_ptr jest zastąpiona std::unique_ptr i btw. std::unique_ptr działa dla tablic. Potrzebujesz wsparcia C++ 11. I jest już dużo zasobów na temat inteligentnych wskaźników i przenosimy semantykę tam. Główną różnicą pomiędzy auto_ptr i unique_ptr że auto_ptr robi ruch kiedy wywołać konstruktor kopiujący i unique_ptr zabrania konstruktor kopiujący, ale pozwala move podczas wywoływania konstruktora ruch. Dlatego potrzebujesz obsługi C++ 11 z semantyką ruchu.

1

Stroustrup omawia pytanie "Co to jest auto_ptr i dlaczego nie ma" auto_array "i dochodzi do wniosku, że nie ma potrzeby stosowania drugiego, ponieważ pożądaną funkcjonalność można uzyskać za pomocą wektora.

http://www.stroustrup.com/bs_faq2.html#auto_ptr

Powiązane problemy