C++ std::unique_ptr、默认复制构造函数和抽象类
我有一个表示树对象的类,它使用唯一的指针,一些节点组成树,还有一个函数根据一些参数构造指向抽象节点类的指针(它指向子类,因为抽象节点是抽象的)C++ std::unique_ptr、默认复制构造函数和抽象类,c++,c++11,C++,C++11,我有一个表示树对象的类,它使用唯一的指针,一些节点组成树,还有一个函数根据一些参数构造指向抽象节点类的指针(它指向子类,因为抽象节点是抽象的) 类抽象节点 { 媒介儿童; 公众: 抽象节点(参数…); //其他东西。。。 }; 类树 { 唯一的\u ptr baseNode; //其他东西。。。 } 唯一的\u ptr constructNode(AbstractNodeType); 树中包含abstractNode的各种子类。这些子类为该类中的一些虚拟函数提供了不同的实现 我希望能够通过创建
类抽象节点
{
媒介儿童;
公众:
抽象节点(参数…);
//其他东西。。。
};
类树
{
唯一的\u ptr baseNode;
//其他东西。。。
}
唯一的\u ptr constructNode(AbstractNodeType);
树中包含abstractNode的各种子类。这些子类为该类中的一些虚拟函数提供了不同的实现
我希望能够通过创建一组新的节点来复制我的树,这些节点的类类型与原始树中节点的不同副本相同
问题是: 如果我为深度复制子类的
AbstractNode
类编写自己的复制构造函数,我将不得不为AbstractNode
的所有子类编写复制构造函数,这似乎很烦人,因为唯一不能正确复制的是子指针。在这里使用复制构造函数也会很烦人,因为我认为在调用它们之前,我需要将子对象强制转换为正确的类型
是否有某种方法可以让编译器允许我使用默认的复制构造函数来设置除子类之外的所有内容。它可以将它们作为空指针或其他什么?然后我可以编写一个更简单的函数,它只是递归地添加子元素来复制一棵树
如果这是不可能的,有人知道这个问题的任何非丑陋的解决方案吗?
< P>如果你想为你的类的一部分使用默认行为,并且只为其他的非标准行为增强它,考虑功能和组织上的类分裂: 将所有需要默认行为的元素放入它们自己的子对象(继承的或合成的),以便可以轻松地为它们使用默认的特殊函数,并将其余元素添加到该子对象之外实现留给感兴趣的读者作为练习。您可能希望自己实现一个
值\u ptr
,而不是使用unique\u ptr
。这些设计在过去经常被提出,但在我们有一个标准化的版本之前,要么自己推出,要么找到一个现有的实现
它看起来有点像这样:
template <typename T> struct value_ptr
{
T * ptr;
// provide access interface...
explicit value_ptr(T * p) noexcept : ptr(p) {}
~value_ptr() { delete ptr; }
value_ptr(value_ptr && rhs) noexcept : ptr(rhs.ptr)
{ rhs.ptr = nullptr; }
value_ptr(value_ptr const & rhs) : ptr(rhs.clone()) {}
value_ptr & operator=(value_ptr && rhs) noexcept
{
if (&rhs != this) { delete ptr; ptr = rhs.ptr; rhs.ptr = nullptr; }
return *this;
}
value_ptr & operator=(value_ptr const & rhs)
{
if (&rhs != this) { T * p = rhs.clone(); delete ptr; ptr = p; }
return *this;
}
};
模板结构值\u ptr
{
T*ptr;
//提供访问接口。。。
显式值_ptr(T*p)noexcept:ptr(p){
~value_ptr(){delete ptr;}
value_ptr(value_ptr&&rhs)无例外:ptr(rhs.ptr)
{rhs.ptr=nullptr;}
value_ptr(value_ptr const&rhs):ptr(rhs.clone()){}
值\u ptr&运算符=(值\u ptr&&rhs)无例外
{
如果(&rhs!=this){delete ptr;ptr=rhs.ptr;rhs.ptr=nullptr;}
归还*这个;
}
值\u ptr和运算符=(值\u ptr常量和rhs)
{
如果(&rhs!=this){T*p=rhs.clone();删除ptr;ptr=p;}
归还*这个;
}
};
您可以从一组可克隆节点构建树
struct AbstractNode
{
virtual ~AbstractNode() {}
virtual AbstractNode * clone() const = 0;
std::vector<value_ptr<AbstractNode>> children;
};
struct FooNode : AbstractNode
{
virtual FooNode * clone() const override { return new FooNode(this); }
// ...
};
struct抽象节点
{
虚拟~AbstractNode(){}
虚拟抽象节点*clone()常量=0;
性病媒儿童;
};
结构FooNode:AbstractNode
{
virtual FoonNode*clone()常量重写{返回新的FoonNode(this);}
// ...
};
现在,您的节点可以自动复制,而无需编写任何显式复制构造函数。您需要做的就是通过在每个派生类中重写
clone
来维护规程。解决此问题的典型方法是使用一个虚拟的clone
函数,类似于Kerrek SB在其回答中描述的功能。不过,我不会费心编写自己的value\u ptr
类。根据您的问题,只需重用std::unique_ptr
就更简单了。它需要在AbstractNode
中使用非默认的复制构造函数,但不需要显式或不安全的强制转换:
class AbstractNode
{
std::vector<std::unique_ptr<AbstractNode>> children;
public:
AbstractNode() = default;
virtual ~AbstractNode() = default;
AbstractNode(AbstractNode const& an)
{
children.reserve(an.children.size());
for (auto const& child : an.children)
children.push_back(child->clone());
}
AbstractNode& operator=(AbstractNode const& an)
{
if (this != &an)
{
children.clear();
children.reserve(an.children.size());
for (auto const& child : an.children)
children.push_back(child->clone());
}
return *this;
}
AbstractNode(AbstractNode&&) = default;
AbstractNode& operator=(AbstractNode&&) = default;
// other stuff...
virtual
std::unique_ptr<AbstractNode>
clone() const = 0;
};
我建议让clone
返回unique_ptr
而不是原始指针,以确保新指针在没有所有者的情况下不会暴露
为了完整起见,我还展示了其他特殊成员的样子
起初,我认为在这里使用C++14的make_unique
会很好。它可以在这里使用。但就我个人而言,我认为在这个特殊的例子中,它真的不起作用。Fwiw,下面是它的样子:
virtual
std::unique_ptr<AbstractNode>
clone() const override
{
return std::make_unique<ConcreteNode>(*this);
}
虚拟
std::unique\u ptr
clone()常量重写
{
return std::make_unique(*this);
}
使用
make_unique
您必须首先构造一个unique_ptr
,然后依赖于从它到unique_ptr
的隐式转换。这是正确的,一旦内联完全启用,额外的舞蹈可能会得到优化。但是在这里使用make_unique
似乎是一个不必要的混淆,因为你真正需要的是一个unique_ptr
,它是用一个新的ConcreteNode*构建的。通常的模式是通过你的层次结构进行虚拟克隆
如果这是不可能的,有没有任何人知道的解决这个问题的非丑陋的办法
您还可以使用基于复制构造函数的克隆函数的模板实例化。以下是我在编写的web服务器(pet项目)中使用的解决方案:
#pragma一次
#包括
#包括
#包括
#包括
#包括
名称空间索引{
内联命名空间详细信息{
///@brief-Deep-copy-construct from(Specialized&)*src
///
///@retval nullptr如果src是nullptr
///@retval*src的专用克隆
///
///@note未定义
class ConcreteNode
: public AbstractNode
{
public:
ConcreteNode() = default;
virtual ~ConcreteNode() = default;
ConcreteNode(ConcreteNode const&) = default;
ConcreteNode& operator=(ConcreteNode const&) = default;
ConcreteNode(ConcreteNode&&) = default;
ConcreteNode& operator=(ConcreteNode&&) = default;
// other stuff...
virtual
std::unique_ptr<AbstractNode>
clone() const override
{
return std::unique_ptr<AbstractNode>(new ConcreteNode(*this));
}
};
virtual
std::unique_ptr<AbstractNode>
clone() const override
{
return std::make_unique<ConcreteNode>(*this);
}
#pragma once
#include <memory>
#include <cassert>
#include <functional>
#include <stdexcept>
#include <vector>
namespace stdex {
inline namespace details {
/// @brief Deep copy construct from (Specialized&)*src
///
/// @retval nullptr if src is nullptr
/// @retval Specialized clone of *src
///
/// @note Undefined behavior src does not point to a Specialized*
template<typename Base, typename Specialized>
Base* polymorphic_clone (const Base* src) {
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
if (src == nullptr)
return nullptr;
return new Specialized{ static_cast<const Specialized&>(*src) };
}
}
/// @brief polymorphic reference interface over a base class
///
/// Respects polymorphic behavior of class ref.
/// Instances have deep copy semantics (clone) and
/// "[const] Base&" interface
///
/// @note Not regular: no trivial way to implement non-intrusive equality
///
/// @note safe to use with standard containers
template<typename Base>
class polymorphic final
{
public:
/// Functor capable to convert a Base* to it's specialized type
/// and clone it (intrusive implementation can be used)
typedef std::function<Base* (const Base*)> clone_functor;
/// @brief construct (takes ownership of ptr)
template<typename Specialized, typename CloneSpecialized>
polymorphic(Specialized* ptr, CloneSpecialized functor) noexcept
: instance_{ptr}, clone_{std::move(functor)}
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
static_assert(
std::is_constructible<clone_functor, CloneSpecialized>::value,
"CloneSpecialized is not valid for a clone functor");
}
// not implemented: UB cloning in case client provides specialized ptr
// polymorphic(Base* ptr);
polymorphic()
: polymorphic{ nullptr, clone_functor{} }
{
}
polymorphic(polymorphic&&) = default;
polymorphic(const polymorphic& other)
: polymorphic{std::move(other.clone())}
{
}
polymorphic& operator=(polymorphic other)
{
std::swap(instance_, other.instance_);
std::swap(clone_, other.clone_);
return *this;
}
~polymorphic() = default;
/// @brief Cast to contained type
/// @pre instance not moved
/// @pre *this initialized with valid instance
operator Base&() const
{
assert(instance_.get());
return *instance_.get();
}
/// @brief Cast to contained type
/// @pre instance not moved
/// @pre *this initialized with valid instance
operator const Base&() const
{
assert(instance_.get());
return *instance_.get();
}
private:
polymorphic clone() const
{
return polymorphic{
clone_(instance_.get()), clone_functor{clone_}
};
}
std::unique_ptr<Base> instance_;
clone_functor clone_;
};
template<typename Base, typename Specialized, typename CF>
polymorphic<Base> to_polymorphic(Specialized&& temp, CF functor)
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
typedef typename polymorphic<Base>::clone_functor clone_functor;
auto ptr_instance = std::unique_ptr<Base>{
new Specialized{std::move(temp)}
};
auto clone_instance = clone_functor{std::move(functor)};
return polymorphic<Base>{ptr_instance.release(), clone_instance};
}
template<typename Base, typename Specialized>
polymorphic<Base> to_polymorphic(Specialized&& temp)
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
return to_polymorphic<Base,Specialized>(
std::move(temp), details::polymorphic_clone<Base,Specialized>
);
}
template<typename Base, typename Specialized, typename ...Args>
polymorphic<Base> to_polymorphic(Args ...args)
{
static_assert(std::is_constructible<Specialized, Args...>::value,
"Cannot instantiate Specialized from arguments");
return to_polymorphic<Base,Specialized>(
std::move(Specialized{std::forward<Args...>(args...)}));
}
template<typename Base> using polymorphic_vector =
std::vector<polymorphic<Base>>;
template<typename Base, typename ...Args>
polymorphic_vector<Base> to_polymorphic_vector(Args&& ...args)
{
return polymorphic_vector<Base>{to_polymorphic<Base>(args)...};
}
} // stdex
stdex::polymorphic_vector<view> views = // explicit type for clarity
stdex::to_polymorphic_vector(
echo_view{"/echo"}, // class echo_view : public view
directory_view{"/static_files"} // class directory_view : public view
);
for(auto& v: views)
if(v.matches(reuqest.url())) // bool view::matches(...);
auto response = view.handle(request); // virtual view::handle(...) = 0;