我有一个用C ++编写的自定义通用序列化系统,我处理了内在函数,std::string
和包含它们的结构。但是,对于包含std::vector<byte>
的内存流类,我希望能够在其中存储和检索std::shared_ptr<T>
(其中T
是从{派生的Abstract
的任何类{1}})。当然,我想要一个解决方案而不使用Boost ,因为它会打败我的意图。
正如Addevent alertDialog所述:
使用另一个shared_ptr拥有的原始底层指针构造一个新的shared_ptr会导致未定义的行为。
我到目前为止唯一(hacky)解决方案是二进制内存流类具有由原始指针本身引用的std::shared_ptr<Abstract>
的小查找表,使得读取和写出它们相当简单,所有权/参考计数是可靠的。然后序列化原始指针变得可行/有用。
但是,所有权/引用计数并不重要,因为它保证用例。如果有一个只使用std::vector<byte>
的解决方案,我会认为这是一种更优雅的方法,因为它可以提供其他用例。
答案 0 :(得分:2)
由于序列化/反序列化过程在同一进程(即相同的内存空间)中发生,因此您可以将原始内存指针存储为流中的二进制数据。考虑下面的想法,写作一个简单的演示。
不幸的是,std::enable_shared_from_this
不允许手动递增/递减引用计数器,因为它只是存储一个弱引用,它无法在内部销毁ref == 0
上的对象。这就是为什么我们必须进行手动引用管理,特别是对于字节流中的实例。
class Abstract : public std::enable_shared_from_this<Abstract> {
public:
Abstract() : _count(0) {}
~Abstract() { cout << "I am destoryed" << endl; }
void incrementStreamRef() {
std::lock_guard<std::mutex> lock(_mutex);
if (!_count) {
_guard = this->shared_from_this();
}
++_count;
};
void decrementStreamRef() {
std::lock_guard<std::mutex> lock(_mutex);
if (_count == 0)
return;
if (_count == 1) {
if (_guard.use_count() == 1) {
// After this call `this` will be destroyed
_guard.reset();
return;
}
_guard.reset();
}
--_count;
};
private:
std::mutex _mutex;
std::shared_ptr<Abstract> _guard;
std::size_t _count;
};
void addAbstractToStream(std::vector<uint8_t>& byteStream, Abstract* abstract) {
abstract->incrementStreamRef();
auto offset = byteStream.size();
try {
// 1 byte for type identification
byteStream.resize(offset + sizeof(abstract) + 1);
byteStream[offset]
= 0xEE; // Means the next bytes are the raw pointer to an Abstract instance
++offset;
// Add the raw pointer to the stream
// prealocate memory here
// byteStream.push_back(....;
// ....
} catch (...) {
abstract->decrementStreamRef();
return;
}
std::memcpy(byteStream.data() + static_cast<std::ptrdiff_t>(offset),
(void*)&abstract,
sizeof(abstract));
}
void removeAbstractFromStream(std::vector<uint8_t>& byteStream, std::size_t offset) {
Abstract* abstract;
std::memcpy((void*)&abstract,
byteStream.data() + static_cast<std::ptrdiff_t>(offset),
sizeof(abstract));
abstract->decrementStreamRef();
}
void tryMe(std::vector<uint8_t>& byteStream) {
// Must not be destoryed when we leave the scope
auto abstract = std::make_shared<Abstract>();
addAbstractToStream(byteStream, abstract.get());
cout << "Scope is about to be left" << endl;
}
int main() {
// Always walk over the stream and use `removeAbstractFromStream`
std::vector<uint8_t> byteStream;
// `try` to always clean the byte stream
// Of course RAII is much better
try {
// Do some work with the stream
} catch (...) {
removeAbstractFromStream(byteStream, 1);
throw;
}
tryMe(byteStream);
cout << "Main is about to be left" << endl;
removeAbstractFromStream(byteStream, 1);
cout << "Main is even closer to be left" << endl;
return 0;
}
当然,如果线程安全性不是问题,那么更精细的锁定可能没问题,或者完全丢弃。请在生产中使用之前修改角落案例的代码。