c++ - 创建boost::shared_ptr的深层拷贝

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

我有shared_ptr的两个 vector ;

typedef boost::shared_ptr <A> someptr;
std::vector<someptr>src;
std::vector<someptr>dest;
如果我使用dest = src复制,这两个 vector 元素共享相同的内存位置,这会增加指针的ref计数。它们都指向相同的位置,并且一个 vector 元素的任何变化都会影响另一个。我知道这是浅拷贝,这是预期的行为。
但是,如果我想为具有不同存储位置的dest vector 元素创建深拷贝,该怎么办?如何实现呢?
Boost是否具有实现此目的的功能?

最佳答案

当然。最简单的:

#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<unique_ptr>这在概念上类似,但是需要您做更多的工作:
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<unique_ptr>但是“更轻松”?
如果您愿意,可以使用一些“魔术”使之更简单:
  • 使用加速范围
    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 PolyContainer
    这不会保留顺序,但是它的优点是您不必提出克隆逻辑,并且可以按类型等进行智能迭代:
    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
    

    关于c++ - 创建boost::shared_ptr的深层拷贝,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62596539/

    相关文章:

    c++ - 在 OpenVM 上构建 Boost

    c++ - 如何为 Borland 编译器重写 C++ 模板代码

    C++ 非致命异常处理

    c++ - 是否可以使用不涉及 Alt 或 Ctrl 或 Shift 的两个键的快捷方式?

    c++ - 我们在 C++ 中使用 "inherit"构造函数吗? "inheriting"的确切定义是什么

    c++ - 如何创建一个带有可变数量参数的模板函数,将参数传递给对象的正确构造函数?

    c++ - 模板函数参数类型中的模板标识符与 decltype

    c++ - 对集合的键类型使用比较函数会导致运行时错误

    c++ - 在现有数据结构上使用BGL算法(作为 vector <Object *>的边和顶点)需要什么?

    c++ - boost::posix_time::ptime 是固定大小的吗?