boost::序列化如何与c++ 11中的std::shared_ptr一起使用?
How can boost::serialization be used with std::shared_ptr from C++11?
我知道有一个Boost模块用于boost::shared_ptr
的序列化,但我找不到std::shared_ptr
的任何内容。
而且,我不知道如何容易地实现它。恐怕下面的代码
namespace boost{namespace serialization{
template<class Archive, class T>
inline void serialize(Archive & ar, std::shared_ptr<T> &t, const unsigned int version)
{
if(Archive::is_loading::value) {T*r;ar>>r;t=r;}
else {ar<<t.get();}
}
}}//namespaces
是行不通的。实际上,如果某个对象被多次引用,它将被加载ar>>r
的第一次运行,之后只是一个指针将被复制。然而,我们将创建多个指向它的shared_ptr
对象,因此将多次销毁它。
有什么想法吗?
关于我正在使用的系统的一些技术细节:
- 操作系统:Ubuntu 11.10 (x64)
- 编译器:g++ (Ubuntu/Linaro 4.6.1-9ubuntu) 4.6.1
- boost版本:1.46.1(安装
sudo apt-get install libboost-dev
)
从Boost 1.56开始,序列化库已经内置了对std::shared_ptr的支持。如果可以使用该库的最新版本,则不需要实现自己的序列化辅助函数。
我终于找到了如何使用boost序列化std::shared_ptr的解决方案。您所需要的只是下面这段代码(解释如下):
#include <boost/serialization/split_free.hpp>
#include <boost/unordered_map.hpp>
//---/ Wrapper for std::shared_ptr<> /------------------------------------------
namespace boost { namespace serialization {
template<class Archive, class Type>
void save(Archive & archive, const std::shared_ptr<Type> & value, const unsigned int /*version*/)
{
Type *data = value.get();
archive << data;
}
template<class Archive, class Type>
void load(Archive & archive, std::shared_ptr<Type> & value, const unsigned int /*version*/)
{
Type *data;
archive >> data;
typedef std::weak_ptr<Type> WeakPtr;
static boost::unordered_map<void*, WeakPtr> hash;
if (hash[data].expired())
{
value = std::shared_ptr<Type>(data);
hash[data] = value;
}
else value = hash[data].lock();
}
template<class Archive, class Type>
inline void serialize(Archive & archive, std::shared_ptr<Type> & value, const unsigned int version)
{
split_free(archive, value, version);
}
}}
这段代码只是在函数save()中序列化std::shared_ptr所管理的对象。如果多个std::shared_ptr实例指向同一个对象,boost序列化将自动注意只存储一次。神奇的事情发生在load()中,其中boost序列化返回指向对象(data)的原始指针。这个原始指针在哈希中查找,哈希为每个原始指针保存一个weak_ptr。如果散列中的weak_ptr过期了,我们可以安全地创建一个新的shared_ptr实例,让它管理原始指针并在散列中存储一个weak_ptr。如果weak_ptr没有过期,我们只需锁定它以返回shared_ptr。这样引用计数是正确的。
序列化由boost提供,而不是由标准库提供,尽管shared_ptr
包含在标准中,但它是TR1(技术报告1)的一部分。
TR1目前还没有序列化。因此,我建议您使用boost的共享指针。
你还没说"行不通"是什么意思;它不能编译?它没有正确加载/存储值?它不…什么?
我可以在这里发现两个问题,其中一个可能是你故意设计的一部分。
首先,在加载过程中没有设置正确的指针。让我们来分析一下:
inline void serialize(Archive & ar, std::shared_ptr<T> &t, const unsigned int version) {
if (1) { //unimportant
T* r;
ar >> r;
t = r;
}
}
当你创建std::shared_ptr的对象时,你是在实例化一个类模板来提供类似指针的功能(如你所知)。如果使用int类型,它将作为int指针工作。然而,简单地将类型作为T传递并不意味着用该类型创建的指针将自动使用该模板;实际上,你用T* r创建了一个空指针,它也可以是int *r。然后用new初始化失败;R可以指向任何地方。如果用new正确地初始化它,你可以得到创建/删除该对象的正确引用计数;这是std::shared_ptr在我看来不值得花费精力的地方。我认为赋值从一个裸指针计数作为第二个引用,而不是第一个,但我可能是错的?不管怎样,这不是问题所在。你可能会破坏堆;编译器应该在使用未初始化的指针时发出警告,很奇怪它没有这样做。我希望你没有关闭警告。
如果我没记错的话,r的声明需要替换为:
std::shared_ptr<T> r = new std::shared_ptr<T>;
虽然它可能是
std::shared_ptr<T> r = new std::shared_ptr<T>(r());
我已经有一段时间没有使用shared_ptr了
顺便说一下,TR1已经问世至少2年了。它基于boost的shared_ptr。我不知道你为什么使用Boost 1.46,但我认为它是在shared_ptr成为标准的一部分的时候出来的?所以它应该是兼容的…?
无论如何,第二个潜在的错误来自t = r;
我假设-不正确?-你希望通过重新赋值来减少t的引用计数(并且可能破坏t所指向的对象)。如果你想复制它,你当然会使用:
*t = *r;
并确保复制构造函数正常工作。
最新版本的Boost Serialization包括对所有标准库智能指针的支持。
这是对denim解决方案的改进,后者支持加载指向相同内存但类型不同的shared_ptr。当存档同时包含指向同一对象的shared_ptr和shared_ptr时,可能出现此问题,其中A继承自b。
namespace boost {
namespace serialization {
template<class Archive, class Type>
void save(Archive & archive, const std::shared_ptr<Type> & value, const unsigned int /*version*/)
{
Type *data = value.get();
archive << data;
}
static std::map<void*, std::weak_ptr<void>> hash;
template<class Archive, class Type>
void load(Archive & archive, std::shared_ptr<Type> & value, const unsigned int /*version*/)
{
Type *data;
archive >> data;
if (hash[data].expired())
{
std::shared_ptr<void> ptr(data);
value = static_pointer_cast<Type>(ptr);
hash[data] = ptr;
}
else value = static_pointer_cast<Type>(hash[data].lock());
}
template<class Archive, class Type>
inline void serialize(Archive & archive, std::shared_ptr<Type> & value, const unsigned int version)
{
split_free(archive, value, version);
}
}}
作为这种实现的弱点-一个大地图。
这是基于boost共享指针头(例如基于<boost/serialization/shared_ptr.hpp>
)滚动自己的结果。
copy &将下面的内容粘贴到头文件中,并包括:
#ifndef BOOST_SERIALIZATION_STD_SHARED_PTR_HPP
#define BOOST_SERIALIZATION_STD_SHARED_PTR_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// shared_ptr.hpp: serialization for boost shared pointer
// (C) Copyright 2004 Robert Ramey and Martin Ecker
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org for updates, documentation, and revision history.
#include <cstddef> // NULL
#include <boost/config.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/integral_c_tag.hpp>
#include <boost/detail/workaround.hpp>
#include <memory>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/tracking.hpp>
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// shared_ptr serialization traits
// version 1 to distinguish from boost 1.32 version. Note: we can only do this
// for a template when the compiler supports partial template specialization
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
namespace boost {
namespace serialization{
template<class T>
struct version< ::std::shared_ptr< T > > {
typedef mpl::integral_c_tag tag;
#if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3206))
typedef BOOST_DEDUCED_TYPENAME mpl::int_<1> type;
#else
typedef mpl::int_<1> type;
#endif
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570))
BOOST_STATIC_CONSTANT(int, value = 1);
#else
BOOST_STATIC_CONSTANT(int, value = type::value);
#endif
};
// don't track shared pointers
template<class T>
struct tracking_level< ::std::shared_ptr< T > > {
typedef mpl::integral_c_tag tag;
#if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3206))
typedef BOOST_DEDUCED_TYPENAME mpl::int_< ::boost::serialization::track_never> type;
#else
typedef mpl::int_< ::boost::serialization::track_never> type;
#endif
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570))
BOOST_STATIC_CONSTANT(int, value = ::boost::serialization::track_never);
#else
BOOST_STATIC_CONSTANT(int, value = type::value);
#endif
};
}}
#define BOOST_SERIALIZATION_SHARED_PTR(T)
#else
// define macro to let users of these compilers do this
#define BOOST_SERIALIZATION_SHARED_PTR(T)
BOOST_CLASS_VERSION(
::std::shared_ptr< T >,
1
)
BOOST_CLASS_TRACKING(
::std::shared_ptr< T >,
::boost::serialization::track_never
)
/**/
#endif
namespace boost {
namespace serialization{
#ifndef BOOST_SERIALIZATION_SHARED_PTR_HPP
struct null_deleter {
void operator()(void const *) const {}
};
#endif
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// serialization for shared_ptr
template<class Archive, class T>
inline void save(
Archive & ar,
const std::shared_ptr< T > &t,
const unsigned int /* file_version */
){
// The most common cause of trapping here would be serializing
// something like shared_ptr<int>. This occurs because int
// is never tracked by default. Wrap int in a trackable type
BOOST_STATIC_ASSERT((tracking_level< T >::value != track_never));
const T * t_ptr = t.get();
ar << boost::serialization::make_nvp("px", t_ptr);
}
template<class Archive, class T>
inline void load(
Archive & ar,
std::shared_ptr< T > &t,
const unsigned int /*file_version*/
){
// The most common cause of trapping here would be serializing
// something like shared_ptr<int>. This occurs because int
// is never tracked by default. Wrap int in a trackable type
BOOST_STATIC_ASSERT((tracking_level< T >::value != track_never));
T* r;
ar >> boost::serialization::make_nvp("px", r);
ar.reset(t,r);
}
template<class Archive, class T>
inline void serialize(
Archive & ar,
std::shared_ptr< T > &t,
const unsigned int file_version
){
// correct shared_ptr serialization depends upon object tracking
// being used.
BOOST_STATIC_ASSERT(
boost::serialization::tracking_level< T >::value
!= boost::serialization::track_never
);
boost::serialization::split_free(ar, t, file_version);
}
} // namespace serialization
} // namespace boost
#endif // BOOST_SERIALIZATION_STD_SHARED_PTR_HPP
您可以在这里查看与<boost/serialization/shared_ptr.hpp>
的差异
,
- 重命名为include guard
- 更改
boost::shared_ptr
为std::shared_ptr
- 包含
<memory>
代替<boost/shared_ptr.hpp>
- 保护
null_deleter
从重新定义,如果你也使用boost::shared_ptr
- 删除了
BOOST_SERIALIZATION_SHARED_PTR_132_HPP
-不管那是关于什么?
到目前为止,这似乎工作得很好
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- dopen():不以 root 身份运行时"failed to map segment from shared object"
- C++中的指针否定 (!ptr == NULL)
- 从const ptr*转换为ptr*时出现问题
- 无法使用 libtool 将 -shared 参数传递给 g++
- boost::shared_ptr和std::shared-ptr的同居
- 我可以用std::shared_ptr而不是boost::shared-ptr构建boost库吗
- shared-ptr-C++shared_ptr与unique_ptr用于资源管理