2016-04-24 2 views
1

Я узнал, что unique_ptr может указывать на уже существующий объект. Например, я могу это сделать:Сильное динамическое распределение Си ++ с уникальным_ptr?

class Foo { 
public: 
    Foo(int nb) : nb_(nb) {} 
private: 
    int nb_; 
}; 

int main() { 
    Foo f1(2); 
    Foo* ptr1(&f1); 
    unique_ptr<Foo> s_ptr1(&f1); 

    return 0; 
} 

Мой вопрос:

Если я создаю класс с unique_ptr < Bar>, как члены данных (где Bar является класс, где был удален конструктор копирования) и конструктор, который принимает указатели в качестве аргумента, могу ли я запретить пользователю передавать уже существующий объект/переменную в качестве аргумента (в этом конструкторе) (то есть заставить его использовать новое ключевое слово)? Потому что, если он это сделает, я не смогу гарантировать состояние valide моих объектов класса (пользователь все еще может изменять элементы данных с их адресом извне класса) .. и я не могу скопировать содержимое Бар в другую область памяти. не

Пример:

class Bar { 
public: 
    Bar(/* arguments */) { /* data members allocation */ } 
    Bar(Bar const& b) = delete; 

    /* Other member functions */ 

private: 
    /* data members */ 
}; 

class Bar_Ptr { 
public: 
    Bar_Ptr(Bar* ptr) { 
    if (ptr != nullptr) { ptr_ = unique_ptr<Bar> (ptr); } 
    } /* The user can still pass the address of an already existing Bar ... */ 

    /* Other member functions */ 

private: 
    unique_ptr<Bar> ptr_; 
}; 
+1

«Но я не смог бы сделать это с помощью shared_ptr. *« ... почему бы и нет? Насколько я знаю, конструкторы 'shared_ptr' не имеют никакой защиты от этого. –

+0

"* и я не могу скопировать содержимое Bar в другую область памяти *« Можете ли вы действительно сделать это в любом случае? 'unique_ptr' не копируется. –

+0

О да, вы правы в первую очередь. Раньше я запускал небольшой тестовый файл, и он не компилировался. Должно быть, было что-то еще. Во-вторых, я подумал о том, чтобы взять объект в качестве аргумента и просто объявить unique_ptr, указывающий на динамически выделенную память (которая будет содержать копию Bar) внутри конструктора, но это невозможно, если конструктор копирования удален. – Desura

ответ

3

Вы не можете помешать программистам делать глупые вещи. Оба std::unique_ptr и std::shared_ptr содержат возможность создания экземпляра с существующим ptr. Я даже видел случаи, когда пользовательский удалён передается, чтобы предотвратить удаление. (Shared ptr более изящный для этих случаев)

Итак, если у вас есть указатель, вы должны знать его право собственности. Вот почему я предпочитаю использовать std::unique_ptr, std::shared_ptr и std::weak_ptr для указателей «владеющих», в то время как необработанные указатели представляют собой указатели, не являющиеся собственниками.Если вы распространяете это на место, где создается объект, большинство статических анализаторов могут сказать вам, что вы допустили ошибку.

Поэтому я хотел бы переписать класс Bar_ptr на что-то вроде:

class Bar_ptr { 
public: 
    explicit Bar_ptr(std::unique_ptr<Bar> &&bar) 
     : ptr(std::move(bar)) {} 
    // ... 
} 

С этого API вашего класса навязывает передачу права собственности, и это до вызывающей, чтобы обеспечить правильный unique_ptr. Другими словами, вы не должны беспокоиться о передаче указателя, который не выделен.

Никто не мешает абоненту из письма:

Bar bar{}; 
Bar_ptr barPtr{std::unique_ptr<Bar>{&bar}}; 

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

+0

Я думал о перемещении умного указателя, но тогда, если пользователь решил попробовать использовать старый указатель в любом случае (например, если он не знает, что я перемещаю его указатель)? Я могу помешать ему это сделать. Это моя вина или его вина? – Desura

+1

Если вы принимаете unique_ptr, он должен переместить его, чтобы иметь возможность вызвать вашу функцию. Так что это полностью ошибка вызывающих, если он не называет ваш API правильным способом. Вы не можете сделать это более понятным, чем путем принятия std :: unique_ptr – JVApen

3

Нет, вы не можете. Вы не можете помешать людям делать глупые вещи. Объявите шаблонную функцию, которая возвращает новый объект на основе шаблонного параметра.

1

Я видел нечто подобное раньше. Фокус в том, что вы создаете функцию (назовем ее make_unique), которая берет объект (не указатель, объект, поэтому, возможно, с неявным конструктором, он может «принимать» аргументы конструктора класса), и эта функция будет создавать и возвращать unique_ptr. Что-то вроде этого:

template <class T> std::unique_ptr<T> make_unique(T b); 

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

1

Вы не можете остановить людей от делать неправильные вещи , Но вы можете побудить их поступать правильно. Или, по крайней мере, если они делают не то, сделайте это более очевидным.

Например, с Bar, не позволяйте конструктору снимать голые указатели. Сделайте это unique_ptr s, либо по значению, либо по &&. Таким образом, вы вызываете вызывающего абонента для создания этих unique_ptr. Вы просто перемещаете их в свои переменные-члены.

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

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