我遇到过一种情况,我需要通过接受 const shared_ptr 引用的 setter 来设置库对象的属性。这是一个简单的例子:
// library class
class WidgetProxy {
public:
void setName(const std::shared_ptr<std::string>& name);
// more methods
};
毫无怀疑,我这样使用它:
WidgetProxy widgetProxy(...);
auto name = std::make_shared<std::string>("Turing");
widgetProxy.setName(name);
// continue using `name`
然后我发现 name
setName()
之后已变空称呼。幸运的是,库源代码可用,我能够检查其实现。大致内容如下:
class WidgetImpl {
public:
void setName(std::string name)
{
name_ = std::move(name);
}
private:
std::string name_;
};
void WidgetProxy::setName(const std::shared_ptr<std::string>& name)
{
widgetImpl_.setName(std::move(*name));
}
所以setName()
移出 shared_ptr
包裹的字符串自 shared_ptr
起正式不再禁止模板参数是 std::string
而不是const std::string
.
我的问题:
- 这是一个正常的设计来实现
WidgetProxy::setName()
像这样吗? - 当图书馆用户看到
const shared_ptr<T>&
时,他们通常是否应该预料到这种行为?函数参数?
更新:发布的代码片段已大大简化。在库中,有一种不同的类型代替 std::string。我还省略了对指针有效性的检查。
最佳答案
Is it a normal design to implement setName() like this?
这种实现风格是可以的:
void setName(std::string name)
{
name_ = std::move(name);
}
函数调用首先复制字符串,并将复制的字符串移动到类成员中。生成的代码与传递对字符串的引用,然后复制到数据成员一样高效。
这个不是。我并不不推荐它。
void WidgetProxy::setName(const std::shared_ptr<std::string>& name)
{
widgetImpl_.setName(std::move(*name));
}
有两个原因。 1:如果不保留指针,为什么需要 std::shared_ptr ? 2:操作的最终结果删除被指针持有的字符串。这会影响shared_ptr的所有其他持有者,其中一些可能需要原始字符串的值。
更正确的编写此函数的方法,以及关联的函数调用:
void WidgetProxy::setName(std::string name)
{
widgetImpl_.setName(std::move(name));
}
// call as:
if (strPtr)
proxy.setName(*strPtr); // with strPtr being a std::shared_ptr<std::string>
Should a library user normally expect such behavior when they see a const shared_ptr& function parameter?
没有。这是一种糟糕的库编码方式。如果调用者出于任何原因希望保留该字符串,他必须使用原始字符串的拷贝创建一个shared_ptr。另外,库代码甚至不检查shared_ptr是否持有有效的指针!非常非常顽皮。
关于c++ - 修改const shared_ptr&传递的数据可以吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45617870/