Оператор перегрузки = пользовательского типа с элементом данных unique_ptr - PullRequest
0 голосов
/ 24 октября 2018

У меня есть определенный пользователем класс, в котором есть std::unique_ptr член.Я пытаюсь перегрузить оператор присваивания для нового объекта того же типа члену unique_ptr или присвоить значение значению ptr.

class object
{
    public:
    // POD types

    object& operator=(const object& _obj);

    std::unique_ptr<baseClass> ptr;
} 

Я пытался использовать:

std::unique_ptr::swap()

но функция подкачки неконстантна, поэтому я попытался присвоить результат:

std::unique_ptr::get()

на ptr и затем вызвать:

std::unique_ptr::release()

но выпуск также не является const, поэтому я не могу гарантировать, что ptr будет уничтожен правильно, так как старый ptr все еще будет владеть.Увы, operator=() для unique_ptr не принимает неконстантную ссылку на другой unique_ptr, поэтому я решил сделать оператор = как для моего класса.

object& object::operator=(const object& _obj)
{
    //POD types use operator= as normal
    ptr.reset(nullptr);

    return *this;
}

И просто вызватьsetPtr() метод позже для установки ptr.Я хотел просто определить тип и просто присвоить ptr, используя:

ptr.reset(new detectedType );

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

Перед публикацией: Я изменилпараметр на object& вместо const object& и все заработало.Это хороший способ исправить эту проблему, или я должен пытаться работать только с константными ссылками на другой класс?

Ответы [ 2 ]

0 голосов
/ 24 октября 2018

Мой вопрос: я пропускаю что-то простое, и есть ли лучший способ перегрузить оператор присваивания для пользовательских типов, которые имеют член unique_ptr?

Да, действительно.Это нарушает понимание того, что делают умные указатели в C++.Обычно вы используете std::unique_ptr, если будет одна и только одна ссылка на ваш объект.По этой причине std_unique_ptr s не могут быть ни копируемыми, ни копируемыми.Если вы вручную создадите две копии std::unique_ptr, как вы описали в своем посте, используя ptr.get(), вы окажетесь в недопустимом состоянии, потому что, как только одна копия покидает область действия, деструктор std::unique_ptr также разрушит объектон указывает, оставляя другой std::unqiue_ptr в недопустимом состоянии (он указывает на свободную ячейку памяти).Поэтому, если может быть несколько ссылок на ваш объект, вам, вероятно, следует использовать std::shared_ptr.

В ваших примерах вопрос состоит в том, чего вы хотите достичь?

Если вы хотите использовать свой оператор присваиваниятаким образом, что после вызова a = b, a.ptr и b.ptr указывают на один и тот же объект (т.е. сохраняют один и тот же указатель), используйте общий указатель.Вам определенно НЕ следует использовать уникальный указатель в этом случае, так как вызов деструктора для a или b приведет к разрушению указанного объекта, оставив a или b в недопустимом состоянии.

Если вы хотите, чтобы после вызова a = b, a.ptr указывал на независимую копию *(b.ptr), вы действительно можете использовать уникальный указатель и реализовать свой оператор присваивания, как этот (при условии, что baseClass является копируемым):

object& object::operator=(const object& _obj)
{
    //POD types use operator= as normal
    ptr.reset(new baseClass(*_obj.ptr));

    return *this;
}

Обратите внимание, что это работает, только если baseClass равно final (маловероятно, учитывая имя ...), то есть ptr не будет хранить указатели на объекты производного класса.В противном случае это приводит к срезам , на что указывает BenVoigt.

Наконец, обычно ожидается, что вызов a = b действительно не изменит b.Поэтому сделать изменяемый элемент ptr и использовать swap звучит для меня как плохая идея.Если вы хотите добиться такого поведения из-за соображений производительности (или других), я бы предложил вместо этого ввести оператор присваивания перемещения

object& object::operator=(object&& _obj)
{
    //POD types use operator= as normal
    ptr = std::move(_obj.ptr);

    return *this;
}

и вызвать a = std::move(b), что дает понять, что b может закончитьсяв другом состоянии.

0 голосов
/ 24 октября 2018

Это хороший вариант использования для ключевого слова mutable.Если вы объявляете ptr как mutable, вы указываете, что даже когда экземпляр вашего класса семантически константен, он должен выполнять неконстантные операции с конкретным элементом данных.Объявите свой класс следующим образом, и вы сможете реализовать свою первоначальную идею использования swap:

class object
{
    public:
    // POD types

    object& operator=(const object& _obj);

    mutable std::unique_ptr<baseClass> ptr;
} 
...