修改数据是否由 const shared_ptr&Ok 传递?
Is modifying data passed by const shared_ptr& Ok?
我遇到过一种情况,我需要通过接受常量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。我还省略了对指针有效性的检查。
像这样实现 setName(( 是一个正常的设计吗?
此实现样式是可以的:
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:操作的最终结果删除了 pointee 持有的字符串。 这会影响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>
库用户在看到 const shared_ptr 和函数参数时通常应该期待这种行为吗?
不。 这是一种编写库代码的糟糕方式。 如果调用方出于任何原因希望保留字符串,则必须使用原始字符串的副本创建一个shared_ptr。 另外,库代码甚至不会检查shared_ptr是否包含有效的指针! 非常非常调皮。
你误解了这意味着什么:
class WidgetProxy {
public:
void setName(const std::shared_ptr<std::string>& name);
};
setName
引用一个可能可变的共享指针,它无权修改该指针。 此共享指针引用可变字符串。
这意味着在setName
中,每当控制从编译器可见的内容中流出时,name
的指针和有效性可能会更改(并且,您应该检查它是否没有(。
此可能可变的共享指针的不可变视图所指向的值是完全可变的。 您具有修改它的完全权限。
一些替代方案:
class WidgetProxy {
public:
void setName(std::shared_ptr<std::string> name);
};
这是指向可变字符串的本地共享指针。 它只能在本地修改,除非你泄漏了对它的引用。 引用的数据由任何其他代码操作,并且必须假定在保留本地上下文时被修改。 但是,除非您亲自清除它,否则它将在setName
函数的生存期内保持有效指针。
class WidgetProxy {
public:
void setName(std::shared_ptr<std::string const> name);
};
这是指向您没有突变权限的字符串的本地共享指针。 如果它实际上在您离开本地代码的任何时候都是可变的,那么具有共享指向它的其他人可以修改它,并且应该假定它正在这样做。
class WidgetProxy {
public:
void setName(std::string name);
};
这是字符缓冲区的本地副本,其他人无法在函数中修改该缓冲区,并且您拥有该缓冲区。
class WidgetProxy {
public:
void setName(std::string const& name);
};
这是对可能可变的外部std::string
的引用,每次在函数中保留本地代码时都必须假定更改该外部。
就个人而言,我认为WidgetProxy
没有理由通过shared_ptr
或const&
进行论点. 它不使用参数的共享性,也不希望在其上远程更改值。 这是一个它将消耗的"接收器"参数,并且移动对象的成本很低。
WidgetProxy::setName
应该采取std::string
. 移动成本低的数据的下沉论点应按值计算。 在这里使用智能指针似乎是一个可怕的想法;为什么要用shared_ptr
使你的生活复杂化?
WidgetImpl::setName()
以这种方式实现是完全可以的,因为它是从本地参数移动的。
以这种方式实现WidgetProxy::setName
只是一个错误,因为您实际上不能期望shared_ptr
管理的对象是可移动的。
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 为什么打印"OK"会改变输出?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- 句柄OK全部崩溃
- C++中的指针否定 (!ptr == NULL)
- 从const ptr*转换为ptr*时出现问题
- 这是MSVC 2013中具有共享PTR的单例的正确实现吗?
- 对唯一 ptr 无效读取的引用向量
- C++ 类型转换基础 PTR 到派生 PTR 保存在引用类中