简体   繁体   English

创建 boost::shared_ptr 的深层副本

[英]Create Deep copy of boost::shared_ptr

I have two vectors of shared_ptr;我有两个 shared_ptr 向量;

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

If I copy using dest=src,both the vector elements are sharing the same memory location which increases the ref count of pointer.They both point to common location and any change in elements of one vector will affect the other.I understood that this is a shallow copy and it is the intended behaviour.如果我使用 dest=src 复制,两个向量元素共享相同的 memory 位置,这会增加指针的引用计数。它们都指向公共位置,一个向量元素的任何变化都会影响另一个。我知道这是浅拷贝,这是预期的行为。

But if I want to create a deep copy for dest vector elements with different memory location,what should I do?How to achieve this?但是,如果我想为具有不同 memory 位置的 dest 向量元素创建一个深拷贝,我该怎么办?如何实现?

Does boost have a function to achieve this?升压是否有 function 来实现这一目标?

Sure.当然。 Simplest:最简单的:

#include <vector>
#include <memory>

struct T {};

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

Of course, there will be a reason why you had the shared_ptr .当然,您拥有shared_ptr是有原因的。 Eg when the objects aren't movable and/or runtime polymorphic.例如,当对象不可移动和/或运行时多态时。


Enter Pointer Container输入指针容器

Boost Pointer Container satisfies this need. Boost Pointer Container 满足了这一需求。 It allows you to customize the way in which elements should be cloned (see eg How does boost::ptr_vector deep copy the underlying objects? or the docs ).它允许您自定义应该克隆元素的方式(参见例如boost::ptr_vector 如何深度复制底层对象?文档)。

Simple example: Live On Coliru简单示例: 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(); 
}

Prints印刷

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

std::vector<unique_ptr>

This is conceptually similar, but requires more work by you:这在概念上是相似的,但需要您做更多的工作:

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(); 
}

Prints:印刷:

a: T U T
b: T U T

vector<unique_ptr> but "easier"? vector<unique_ptr>但“更容易”?

If you like you can use some "magic" to make that simpler:如果您愿意,可以使用一些“魔术”来简化:

  • Using Boost Range使用升压范围

    Live On Coliru住在科利鲁

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

    Using Ranges v3 instead because to_vector hasn't been standardized (yet)改用 Ranges v3,因为to_vector尚未标准化(还)

    Live On Compiler Explorer Live On 编译器资源管理器

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

Fully automatic: Boost PolyContainer全自动: Boost PolyContainer

This doesn't retain order, but it has the advantage that you don't have to come up with the cloning logic, and you can do smart iteration by type etc.:这不保留顺序,但它的优点是您不必提出克隆逻辑,并且您可以按类型进行智能迭代等:

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(); 
}

Prints印刷

a: U T T
b: U T T

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM