У меня есть определенный пользователем класс, в котором есть член 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()

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

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

    return *this;
}

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

ptr.reset(new detectedType );

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

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

1
Nathan Krone 24 Окт 2018 в 05:59

2 ответа

Лучший ответ

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

Это действительно так. Это сводится к пониманию того, что делают интеллектуальные указатели в C++. Обычно вы используете std::unique_ptr, если будет одна и только одна ссылка на ваш объект. Это причина того, почему std_unique_ptr не могут быть ни сконструированы копированием, ни назначены копированием. Если вы вручную создадите две копии 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 не будет хранить указатели на объекты производного класса. В противном случае это приведет к нарезке, как указал БенВойгт.

Наконец, обычно ожидается, что вызов 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 может оказаться в другом состоянии.

1
Haatschii 24 Окт 2018 в 13:16

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

class object
{
    public:
    // POD types

    object& operator=(const object& _obj);

    mutable std::unique_ptr<baseClass> ptr;
} 
0
Derek T. Jones 24 Окт 2018 в 04:33
52960477