【问题标题】:Create Deep copy of boost::shared_ptr创建 boost::shared_ptr 的深层副本
【发布时间】:2023-03-05 22:23:01
【问题描述】:

我有两个 shared_ptr 向量;

typedef boost::shared_ptr <A> someptr;
std::vector<someptr>src;
std::vector<someptr>dest;

如果我使用 dest=src 进行复制,两个向量元素共享相同的内存位置,这会增加指针的引用计数。它们都指向公共位置,一个向量元素的任何变化都会影响另一个。我理解这是一个浅拷贝,这是预期的行为。

但是如果我想为不同内存位置的dest向量元素创建一个深拷贝,我应该怎么做?如何实现呢?

boost有实现这个的功能吗?

【问题讨论】:

  • 您的向量是否仅包含 A 的实例,或者还包含派生自它的类?
  • 例如:src 包含 2 个元素。dest 是一个新的空向量,需要深拷贝。它们没有派生类。
  • for (auto const&amp; p : src) dest.push_back(make_shared&lt;A&gt;(*p));
  • Eljay 的解决方案仅在向量不能包含从 A 派生的类时才有效。如果可以,您将需要其他东西。
  • 使用上面的代码语句会抛出错误:showing cannot convert boost::shared_ptr to (const A&)。

标签: c++ c++11 boost shared-ptr


【解决方案1】:

当然。最简单的:

#include <vector>
#include <memory>

struct T {};

int main() {
    std::vector<T> a{100};
    auto b = a; // deep copy all T
}

当然,您拥有shared_ptr 是有原因的。例如。当对象不可移动和/或运行时多态时。


进入指针容器

Boost Pointer Container 满足了这一需求。它允许您自定义克隆元素的方式(参见例如How does boost::ptr_vector deep copy the underlying objects?the docs)。

简单示例:Live On Coliru

#include <vector>
#include <memory>
#include <boost/ptr_container/ptr_vector.hpp>
#include <iostream>

struct Base {
  virtual ~Base() = default; // runtime polymorphic
  virtual char const* foo() const = 0;
};

struct T : Base {
  virtual char const* foo() const override { return "T"; }
};
struct U : Base {
  virtual char const* foo() const override { return "U"; }
};

static inline Base* new_clone(Base const& obj) {
    if (auto* p = dynamic_cast<T const*>(&obj))
        return new T{*p};
    if (auto* p = dynamic_cast<U const*>(&obj))
        return new U{*p};
    return nullptr;
}

int main() {
    boost::ptr_vector<Base> a;
    std::generate_n(std::back_inserter(a), 5, [] { return new T{}; });
    // polymorphic
    a.insert(a.begin()+2, new U{});

    auto b = a; // deep copy all elements, derived from Base

    // not sharing the instances:
    assert(&a.front() != &b.front());

    std::cout << "\na:";
    for (auto& el : a) std::cout << " " << el.foo(); 

    std::cout << "\nb:";
    for (auto& el : b) std::cout << " " << el.foo(); 
}

打印

a: T T U T T T
b: T T U T T T

std::vector&lt;unique_ptr&gt;

这在概念上相似,但需要您做更多的工作:

Live On Coliru

#include <algorithm>
#include <vector>
#include <memory>
#include <iostream>
#include <cassert>

struct Base {
  virtual ~Base() = default; // runtime polymorphic
  virtual char const* foo() const = 0;
};

struct T : Base {
  virtual char const* foo() const override { return "T"; }
};
struct U : Base {
  virtual char const* foo() const override { return "U"; }
};

struct Cloner {
    using Ptr = std::unique_ptr<Base>;
    Ptr operator()(Ptr const& pb) const {
        if (auto* p = dynamic_cast<T const*>(pb.get()))
            return std::make_unique<T>(*p);
        if (auto* p = dynamic_cast<U const*>(pb.get()))
            return std::make_unique<U>(*p);
        return nullptr;
    }
};

int main() {
    std::vector<std::unique_ptr<Base> > a;
    a.push_back(std::make_unique<T>());
    a.push_back(std::make_unique<U>());
    a.push_back(std::make_unique<T>());

    std::vector<std::unique_ptr<Base> > b;

    // deep copy all elements, derived from Base
    Cloner clone;
    std::transform(begin(a), end(a), back_inserter(b), clone);

    // not sharing the instances:
    assert(&*a.front() != &*b.front());

    std::cout << "\na:";
    for (auto& p : a) std::cout << " " << p->foo(); 

    std::cout << "\nb:";
    for (auto& p : b) std::cout << " " << p->foo(); 
}

打印:

a: T U T
b: T U T

vector&lt;unique_ptr&gt; 但“更容易”?

如果你愿意,你可以使用一些“魔法”来简化:

  • 使用增强范围

    Live On Coliru

      // deep copy all elements, derived from Base
      auto b = boost::copy_range<upvec>(a | transformed(Cloner{}));
    
  • 使用标准范围 (c++20)

    改用 Ranges v3,因为 to_vector 尚未标准化(还)

    Live On Compiler Explorer

      // deep copy all elements, derived from Base
      auto b = ranges::to_vector(a | ranges::views::transform(Cloner{}));
    

全自动:Boost PolyCollection

这不保留顺序,但它的优点是你不必想出克隆逻辑,并且可以按类型等进行智能迭代:

Live On Coliru

#include <boost/poly_collection/base_collection.hpp>
#include <memory>
#include <iostream>
#include <cassert>

struct Base {
  virtual ~Base() = default; // runtime polymorphic
  virtual char const* foo() const = 0;
};

struct T : Base {
  virtual char const* foo() const override { return "T"; }
};
struct U : Base {
  virtual char const* foo() const override { return "U"; }
};

int main() {
    using C = boost::poly_collection::base_collection<Base>;
    
    C a;
    a.insert(T{});
    a.insert(U{});
    a.insert(T{});

    // deep copy all elements, derived from Base
    auto b = a;

    // not sharing the instances:
    assert(&*a.begin() != &*b.begin());

    std::cout << "\na:";
    for (auto& p : a) std::cout << " " << p.foo(); 

    std::cout << "\nb:";
    for (auto& p : b) std::cout << " " << p.foo(); 
}

打印

a: U T T
b: U T T

【讨论】:

  • 充实了三个备选方案:ptr_vectorvector<unique_ptr<Base>>boost::poly_collection<Base>。只有后者为您完全克隆。
  • ,非常感谢您分享代码。事实上,我拥有的两个矢量对象属于同一类,一个不是从另一个派生的。每当您将副本复制为 b=a 时,结果在浅拷贝中。我需要从 a 到 b 的深拷贝或按值复制。使用复制构造函数从 a(b(*a)) 复制 b,确实会引发错误。
  • 您能否显示问题所在(也许通过编辑实时样本?)。我不明白你的意思。
  • 在我所有的方法中,复制的向量都是相同的类型,三个副本很深,它们都编译没有错误。
猜你喜欢
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-03
  • 2016-06-21
相关资源
最近更新 更多