17

我有一些当前使用原始指针的代码,我想更改为智能指针。这有助于以各种方式清理代码。无论如何,我有返回对象的工厂方法以及调用者管理它们的责任。所有权不共享,所以我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,我遇到了多态性问题。最初我决定简单地更改Pushto use的定义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 是行不通的。需要从智能指针获取底层指针是否很常见?

谢谢,对不起,如果问题不清楚(请告诉我)。

编辑:我认为我的推送功能有点模棱两可。它复制底层值,实际上并不修改或存储输入对象。

4

4 回答 4

12

最初我决定简单地将 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将其移动到那里。如果不是,则使用原始指针或引用,因为这就是您用于非拥有别名的方法。

于 2012-11-08T12:12:48.543 回答
4

好吧,如果您的函数对(指向)对象本身进行操作并且不需要它的地址,也不需要任何所有权,而且,我猜,总是需要一个有效的对象(传递 a 时失败nullptr),为什么他们需要指针有吗?

正确执行并让他们参考:

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

然后,原始指针和智能指针的调用代码看起来完全相同:

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

编辑:但当然,无论是使用引用还是指针,如果它不获取传递对象的所有权(这会使其从传递的指针中窃取所有权)Push,请不要使用。std::unique_ptr或者一般来说,当指向的对象不被拥有时,不要使用拥有指针,std::shared_ptr在这方面没有什么不同,并且如果没有所有权可以作为std::unique_ptrfor的参数的选择,则更糟糕。PushPush

于 2012-11-08T12:11:12.197 回答
3

如果Push不占用所有权,它可能应该使用引用而不是指针。而且很可能是const一个。所以你会有

Push(*number);

现在,这显然只有在 Push 不会将指针保留在其返回后的任何位置时才有效。如果是这样,我怀疑您应该首先尝试重新考虑所有权。

于 2012-11-08T12:08:51.497 回答
1

这是一个使用唯一指针的多态示例:

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);
}
于 2013-10-10T13:52:12.403 回答