将 object 从局部变量移动到 std::shared_ptr

Move object from local variable to std::shared_ptr

请注意,标题中的“object”和“移动”并不是指 C++ 特定的概念,即 object 是什么以及移动意味着什么一个 object.

我有一个非常简单的结构类型的局部变量,我在其中积累了一些从配置文件中读取的值。

struct Foo
{
    std::string name;
    float someValue;
    int someStuff;
};

// This stores the current settings
std::shared_ptr<Foo> currentFooSetting;

void readSettings()
{
    Foo f;
    f.name = ...;
    f.someValue = ...;
    f.someStuff = ...;
    if (everythingIsOk)
    {
        //TODO:
        //Delete the old object pointed to by currentFooSetting (if it's not pointed to by any other shared_ptr, of course)
        //Allocate some new memory to put f into... or maybe reuse *currentFooSetting if it's now free idc.
        //Copy f to the new memory location
        //
    }
}

如果一切正常,我想将 f 移到堆上并让 currentFooSetting 指向它。 我该怎么做?

看来我可以这样做:

std::shared_ptr<Foo> newFooSetting = std::make_shared<Foo>();
*newFooSetting = f;
currentFooSetting = newFooSetting;

(使用局部变量更好地强调分配、复制和替换之间的分离。) 但即使通读 https://en.cppreference.com/w/cpp/memory/shared_ptr and https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared 我也不知道这是否是这样做的“方式”。

怎么样

if (currentFooSetting) {
    *currentFooSetting = f;
} else {
    currentFooSetting = std::make_shared<Foo>(f);
}

this sort-of 确保您只有一个共享指针,并且一旦它被创建,它的值就会在更新时改变。或者,如果现有 holders-of-the-shared-pointer 应该保留它们的值,只需分配一个新值:

currentFooSetting = std::make_shared<Foo>(f);

这将“分叉”当前设置的视图 -- holders-of-a-shared-pointer 旧值保留旧值,但新共享指针的函数获得新值。

哪个更有意义取决于您的设计(这是 Ted Lyngmo 在评论中提出的问题)。这两个代码片段都假定您的 Foo class 具有合适的(复制)构造函数。

比问题中的尝试更简单:

if (everythingIsOk) {
    currentFooSetting = std::make_shared<Foo>(std::move(f));
}

std::make_shared 将分配动态内存,新对象将通过从 f 移动来初始化。我们也可以复制,但在这种情况下,本地 f 即将超出范围,因此从中移动是安全的。

分配currentFooSetting将导致它销毁先前指向的对象(如果有的话,并且如果没有其他所有者),并且currentFooSetting成为新创建对象的所有者。

这是一个 readSettings 函数模板,支持移动分配给现有 Foo 或无条件移动构造新的 Foo(如果 everythingIsOktrue ):

template<bool Reassign = true>
void readSettings() {
    Foo f;
    
    // ...

    if (everythingIsOk) {
        if constexpr (Reassign) {
            // move assign if you've already createad a shared Foo
            if(currentFooSetting) *currentFooSetting = std::move(f);
            // or move construct a new Foo
            else currentFooSetting = std::make_shared<Foo>(std::move(f));
        } else {
            // always move construct a new Foo
            currentFooSetting = std::make_shared<Foo>(std::move(f));
        }
    }
}

然后使用 readSettings()/readSettings<true>() 移动分配给现有的 FooreadSettings<false>() 总是移动构造一个新的 Foo.