首页 > 解决方案 > 是否可以将智能指针的内存重用于另一个对象?

问题描述

假设我有两种大小完全相同的不相关类型。

struct Foo { ... };
struct Bar { ... };

现在,假设我有一棵树Foo,我想将其转换为Bar.

template <typename Leaf>
struct Tree
{
    Leaf data;
    std::unique_ptr<Tree> lhs;
    std::unique_ptr<Tree> rhs;
};

Tree<Foo> footree = ...;
Tree<Bar> bartree = convert(std::move(footree)); // <--

如何在重用堆分配的内存时执行此转换footree?换句话说,我如何(安全地)将 a 转换Tree<Foo>为 aTree<Bar>而没有额外的内存分配?

这是我实际尝试做的一个简化示例。实际上Foo,并且Bar是具有不同数量的替代类型的递归变体。

标签: c++smart-pointers

解决方案


对于原始指针,这并不难。只需手动触发析构函数Foo并通过放置 new 手动应用构造函数。这enable_if是确保大小和对齐匹配的 SFINAE。

#include <iostream>
#include <vector>

template<typename U, typename V, typename... Args,
         std::enable_if_t<sizeof(U) == sizeof(V) && alignof(U) == alignof(V), int> = 0>
U* repurpose_raw_ptr(V* ptr, Args&&... args)
{
    ptr->~V();
    return new ((void*)ptr) U(std::forward<Args>(args)...);
}

int main()
{
    std::vector<int>* X = new std::vector<int>({1,2,3,4,5});
    std::vector<double>* Y =
        repurpose_raw_ptr<std::vector<double>>(X,
            std::initializer_list<double>{1.,2.,3.,4.,5.});

    std::cout << Y->at(0) << " " << Y->at(4);

    delete Y;

    return 0;
}

您可以使用释放对象所有权的唯一指针方法将该函数应用于“转换” from std::unique_ptr<Foo>to 。就像是:std::unique_ptr<Bar>release()

std::unique_ptr<Bar> X = ...;
std::unique_ptr<Foo> Y = std::unique_ptr<Foo>(repurpose_raw_ptr<Bar>(X.release(),...));

在这里,它假设delete Foo并且delete Bar与破坏原始指针的 Foo/Bar + dealloc 相同,我相信这可能并不总是如此。

一般来说,我不建议这样做,因为很难确保结构在跨平台环境中具有相同的大小/对齐方式。

编辑:以前,错过了您想以某种方式转换FooBar内容。您必须临时将实例复制/移动Foo到其他地方,然后触发内存重新利用。


推荐阅读