2016-12-01 3 views
13

Предположим, у меня есть следующий код в C++:Использует ли это использование std :: make_unique для не-уникальных указателей?

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

Как я понимаю, уникальные указатели используются, чтобы гарантировать, что ресурсы не являются общими. Но в этом случае как p1, так и p2 указывают на тот же пример some.

Пожалуйста, представьте ситуацию.

+1

Ваша ситуация была бы легко диагностирована, если бы вы сами напечатали значения указателя, а не поле 'a'. Или вы сделали «громкий» конструктор копий. Кроме того, вашей основной ошибкой было, вероятно, думать, что make_unique создает указатель на данный объект (здесь локальная переменная 'some'); это не то, что он делает, или для. –

+2

Я совершенно удивлен, что в этом бедном посте так много оборотов. Что случилось с SO? – Walter

ответ

26

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

#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

Стоит отметить, что 'unique_ptr' не имеет средств для обеспечения уникальной собственности. Небрежный программист может легко передать право собственности на один и тот же объект между двумя отдельными 'unique_ptr', что, скорее всего, приведет к неопределенному поведению во время выполнения. – ComicSansMS

+0

@ComicSansMS, вы на 100% правильны. Но я неоднозначно добавляю пример показа, как это можно сделать ... – StoryTeller

+6

Я склонен согласиться. Не нужно показывать людям, как ударить их собственные ноги. – ComicSansMS

16

std::make_unique создает объекты, вызывающие конструктор с указанными аргументами.

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

Таким образом, p1 и p2 2 абсолютно разные указатели, но построены из того же объекта, используя конструктор копирования

1

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

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

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

ДОПОЛНЕНИЕ: Как отметил Реми Лебо, так как C++ 11 рекомендуется использовать функцию std::addressof для этой цели, которая получает фактический адрес объекта, даже если оператор & перегружен:

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

Этот код завершился неудачно, если класс реализует пользовательский 'operator &'.Это то, что было введено 'std :: addressof()': 'std :: cout << std :: addressof (* p1) <<" "<< std :: addressof (* p2) << std :: endl; ' –

+0

Вы правы, спасибо. Для данного примера это не обязательно, но, безусловно, имеет смысл использовать его в любом случае. Я добавил его к ответу. – 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; 
} 

выход:

5 5 
42 5 
Смежные вопросы