2016-12-01 21 views
13

Załóżmy, że mam następujący kod w C++:Czy to użycie std :: make_unique prowadzi do nieunikalnych wskaźników?

#include <memory> 
#include <iostream> 

struct Some { 
     Some(int _a) : a(_a) {} 
     int a; 
}; 

int main() { 
     Some some(5); 

     std::unique_ptr<Some> p1 = std::make_unique<Some>(some); 
     std::unique_ptr<Some> p2 = std::make_unique<Some>(some); 

     std::cout << p1->a << " " << p2->a << std::endl; 
     return 0; 
} 

Jak rozumiem, unikalne wskaźniki są stosowane w celu zagwarantowania, że ​​środki nie są wspólne. Ale w tym przypadku zarówno p1, jak i p2 wskazują na to samo wystąpienie some.

Prosimy o przedstawienie sytuacji.

+1

Twoja sytuacja byłaby łatwa do zdiagnozowania, gdybyś sam wydrukował wartości wskaźników, a nie pole "a" wskazanych osób. Lub wykonałeś "głośny" konstruktor kopii. Ponadto, twoim najbardziej podstawowym błędem było prawdopodobnie myślenie 'make_unique' ustawia wskaźnik na dany obiekt (tutaj lokalna zmienna' some'); to nie jest to, co robi, lub jest przeznaczone. –

+2

Jestem całkowicie zdumiony, że ten biedny post ma tak wiele przebojów. Co jest nie tak z SO? – Walter

Odpowiedz

26

Oni nie wskazują na samym zasobu, że każdy punkt na inny z kopią to. Można zilustrować go usuwając konstruktor kopiujący zobaczyć błąd:

#include <memory> 
#include <iostream> 

struct Some { 
     Some(int _a) : a(_a) {} 
     Some(Some const&) = delete; 
     int a; 
}; 

int main() { 
     Some some(5); 

     std::unique_ptr<Some> p1 = std::make_unique<Some>(some); //error here 
     std::unique_ptr<Some> p2 = std::make_unique<Some>(some); 

     std::cout << p1->a << " " << p2->a << std::endl; 
     return 0; 
} 
+4

Warto jednak podkreślić, że 'unique_ptr' nie ma możliwości egzekwowania wyjątkowej własności. Nieostrożny programista może z łatwością dzielić własność tego samego obiektu między dwie odmienne 'unique_ptr', co najprawdopodobniej spowoduje niezdefiniowane zachowanie w czasie wykonywania. – ComicSansMS

+0

@ComicSansMS, jesteś w 100% poprawny. Ale jestem ambiwalentny jeśli chodzi o dodanie przykładu pokazującego, jak można to zrobić ... – StoryTeller

+6

Ja się zgadzam. Nie trzeba pokazywać ludziom, jak zdmuchnąć własne nogi. – ComicSansMS

16

std::make_unique tworzy obiekty, wywołując konstruktor z określonymi argumentami.

Podałeś Some& jako parametr, a tutaj skopiowano konstruktora i skonstruowano nowy obiekt.

Tak, P1 i P2 są absolutnie różne wskaźniki 2, ale zbudowane z tego samego przedmiotu, używając kopii konstruktora

1

przetestować, czy dwa wskaźniki wskazują na tej samej instancji obiektu, należy porównać lokalizacje one wskazują, zamiast zmiennych składowych obiektu:

std::cout << &(*p1) << " " << &(*p2) << std::endl; 

który pokaże, że rzeczywiście robią nie punkt do tej samej instancji.

Uzupełnienie: Jak podkreślił Remy Lebeau, ponieważ C++ 11 wskazane jest, aby korzystać z funkcji std::addressof do tego celu, który uzyskuje rzeczywisty adres obiektu, nawet jeśli operator & jest przeciążony:

std::cout << std::addressof(*p1) << " " << std::addressof(*p2) << std::endl; 
+2

Ten kod nie powiedzie się, jeśli klasa implementuje niestandardowy 'operator &'.Oto co 'std :: addressof()' zostało wprowadzone w celu rozwiązania: 'std :: cout << std :: addressof (* p1) <<" "<< std :: addressof (* p2) << std :: endl; ' –

+0

Masz rację, dziękuję. Dla danego przykładu nie jest to konieczne, ale z pewnością ma sens, aby go używać. Dodałem go do odpowiedzi. – Meyer

9

both p1 and p2 point to the same instance some

No, they don't.

#include <memory> 
#include <iostream> 

struct Some { 
     Some(int _a) : a(_a) {} 
     int a; 
}; 

int main() { 
     Some some(5); 

     std::unique_ptr<Some> p1 = std::make_unique<Some>(some); 
     std::unique_ptr<Some> p2 = std::make_unique<Some>(some); 

     std::cout << p1->a << " " << p2->a << std::endl; 
     p1->a = 42; 
     std::cout << p1->a << " " << p2->a << std::endl; 
     return 0; 
} 

wyjściowa:

5 5 
42 5