unique_ptr и полиморфизм

У меня есть код, который в настоящее время использует сырые указатели, и я хочу перейти на умные указатели. Это помогает очистить код различными способами. Во всяком случае, у меня есть фабричные методы, которые возвращают объекты и ответственность вызывающего их за управление ими. Владение не разделяется, и поэтому я считаю, unique_ptr подойдет. Все объекты, которые я возвращаю, обычно происходят из одного базового класса, Object,

Например,

class Object { ... };
class Number : public Object { ... };
class String : public Object { ... };

std::unique_ptr<Number> State::NewNumber(double value)
{
return std::unique_ptr<Number>(new Number(this, value));
}

std::unique_ptr<String> State::NewString(const char* value)
{
return std::unique_ptr<String>(new String(this, value));
}

Возвращаемые объекты довольно часто нужно передавать другой функции, которая работает с объектами типа Object (базовый класс). Без каких-либо умных указателей код такой.

void Push(const Object* object) { ... } // push simply pushes the value contained by object onto a stack, which makes a copy of the value
Number* number = NewNumber(5);
Push(number);

При преобразовании этого кода для использования unique_ptrs Я столкнулся с проблемами с полиморфизмом. Изначально я решил просто изменить определение Push использовать unique_ptrs тоже, но это генерирует ошибки компиляции при попытке использовать производные типы. Я мог бы выделить объекты в качестве базового типа, как

std::unique_ptr<Object> number = NewNumber(5);

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

void Push(const Object* object) { ... }
std::unique_ptr<Object> number = NewNumber(5);
Push(number.get());

Теперь о причине публикации. Я хочу знать, это нормальный способ решить проблему, которая у меня была? Это лучше иметь Push работать на unique_ptr против самого объекта? Если так, то как можно решить проблемы полиморфизма? Я предполагаю, что простое приведение ptrs не сработает. Часто ли требуется получить базовый указатель от умного указателя?

Спасибо, извините, если вопрос не ясен (просто дайте мне знать).

редактировать: я думаю, что моя функция Push была немного неоднозначной. Он создает копию базового значения и фактически не изменяет и не сохраняет входной объект.

14

Решение

Изначально я решил просто изменить определение Push для использования
unique_ptrs тоже, но это генерирует ошибки компиляции при попытке использовать
производные типы.

Вы, вероятно, не правильно справились с уникальностью.

void push(std::unique_ptr<int>);
int main() {
std::unique_ptr<int> i;
push(i); // Illegal: tries to copy i.
}

Если это скомпилировано, это будет тривиально нарушать инвариант unique_ptrвот только один unique_ptr владеет объектом, потому что оба i и местный аргумент в push будет владеть этим intтак что это незаконно. unique_ptr является двигаться только, это не копируется. Это не имеет ничего общего с производным в базовое преобразование, которое unique_ptr справляется совершенно правильно.

Если push владеет объектом, затем используйте std::move переместить его туда. Если это не так, используйте необработанный указатель или ссылку, потому что это то, что вы используете для псевдонима, не являющегося владельцем.

11

Другие решения

Что ж, если ваши функции работают с самим (указанным) объектом и не нуждаются в его адресе, не вступают в права собственности и, как я полагаю, всегда нуждаемся в действительном объекте (ошибка при передаче nullptr), почему они вообще используют указатели?

Сделайте это правильно и заставьте их брать ссылки:

void Push(const Object& object) { ... }

Тогда вызывающий код выглядит одинаково для необработанных и умных указателей:

auto number = NewNumber(5);
Push(*number);

РЕДАКТИРОВАТЬ: Но, конечно, независимо от того, используете ли ссылки или указатели, не делайте Push взять std::unique_ptr если он не вступает во владение переданным объектом (что может заставить его украсть владение из переданного указателя). Или вообще не используйте указатели владения, когда указатель на объект не должен быть владельцем, std::shared_ptr ничем не отличается в этом отношении и является столь же худшим выбором, как std::unique_ptr за Pushпараметр, если нет права собственности Push,

4

Если Push не принимает owenrship, скорее всего, вместо указателя следует использовать ссылку. И, скорее всего, const один. Итак, вы будете иметь

Push(*number);

Теперь это очевидно только в том случае, если Push не будет удерживать указатель где-либо после его возврата. Если это так, я подозреваю, что вы должны сначала переосмыслить право собственности.

3

Вот пример полиморфизма с использованием уникального указателя:

vector<unique_ptr<ICreature>> creatures;

creatures.emplace_back(new Human);
creatures.emplace_back(new Fish);

unique_ptr<vector<string>> pLog(new vector<string>());

for each (auto& creature in creatures)
{
auto state = creature->Move(*pLog);
}
1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector