使用类型擦除删除C++11中的void*
Delete void* in C++11 using type erasure
我正试图修复此类中的双重免费或损坏:
struct Holder
{
template <typename T>
Holder(const T& v)
{
_v = new T{};
memcpy(_v, &v, sizeof(T));
_deleter = [this]{
if (_v != nullptr)
{
delete reinterpret_cast<T*>(_v);
_v = nullptr;
}
};
}
template <typename T>
T get()
{
T t;
memcpy(&t, _v, sizeof(T));
return t;
}
~Holder()
{
std::cout << "~Holder() " << std::endl;
_deleter();
}
private:
void* _v;
std::function<void()> _deleter;
};
这个类的目标是保持一个特定类型的值,比如boost::any
。因此,我试图理解安全地释放所有内存的机制。
可能是这行代码:
delete reinterpret_cast<T*>(_v);
没有达到我的预期。。。
***建议后****
我已经使用注释建议重写了代码,并添加了移动构造函数
struct Holder
{
template <typename T>
Holder(const T& v)
{
std::cerr << "create " << N << std::endl;
_v = new T(v);
_deleter = [this]{
if (_v != nullptr)
{
std::cerr << "deleter " << N << std::endl;
delete reinterpret_cast<T*>(_v);
_v = nullptr;
}
};
}
Holder(Holder&& rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
rs._deleter = []{}; //usefull to avoid a bad function call
}
template <typename T>
T get() const
{
return *reinterpret_cast<T*>(_v);
}
~Holder()
{
//std::cout << "~Holder() " << N << std::endl;
_deleter();
}
private:
void* _v;
std::function<void()> _deleter;
};
现在看来是可行的,但我必须管理其他角落的情况:)可能最好的解决方案是使用boost::any:
struct Holder
{
template <typename T>
Holder(const T& v)
{
_v = v;
}
template <typename T>
T get()
{
return boost::any_cast<T>(_v);
}
private:
boost::any _v;
};
但我正在努力了解没有它它它是如何工作的。
这是我的最后一个版本:
struct Holder
{
template <typename T>
Holder(const T& v)
{
std::cerr << "create " << N << std::endl;
_v = new T(v);
_deleter = [](void* ptr){
if (ptr != nullptr)
{
std::cerr << "deleter " << std::endl;
delete reinterpret_cast<T*>(ptr);
}
};
_builder = [](void* &dest, void* src){
dest = new T(*reinterpret_cast<T*>(src));
};
}
Holder(const Holder& rs)
{
std::cerr << "copy constr" << std::endl;
if (this != &rs)
{
rs._builder(_v, rs._v);
_deleter = rs._deleter;
_builder = rs._builder;
}
}
Holder(Holder&& rs)
{
std::cerr << "move constr" << std::endl;
if (this != &rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
_builder = std::move(rs._builder);
rs._deleter = [](void*){};
}
}
Holder& operator=(const Holder& rs)
{
std::cerr << "copy operator" << std::endl;
if (this != &rs)
{
rs._builder(_v, rs._v);
_deleter = rs._deleter;
_builder = rs._builder;
}
return *this;
}
Holder& operator=(Holder&& rs)
{
std::cerr << "move operator" << std::endl;
if (this != &rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
_builder = std::move(rs._builder);
rs._deleter = [](void*){};
}
return *this;
}
template <typename T>
T get() const
{
return *reinterpret_cast<T*>(_v);
}
~Holder()
{
//std::cout << "~Holder() " << N << std::endl;
_deleter(_v);
}
private:
void* _v;
std::function<void(void* ptr)> _deleter;
std::function<void(void* &, void* src)> _builder;
};
不要重新实现马。
using pvoid_holder = std::unique_ptr<void, std::function<void(void*)>>
template<class T>
pvoid_holder pvoid_it( T* t ) {
return { t, [](void* v){ if (v) delete static_cast<T*>(v); } };
}
现在将pvoid_holder
存储在Holder
类中。它将为你处理一生的记忆。
您可以使用裸pvoid_holder
,但它可能具有比您想要的更丰富的接口(例如,它将允许在不更改deleter的情况下更改存储的指针)。
您也可以将std::function
替换为void(*)(void*)
以获得边际性能增益。
这里有一个随机的想法。不过我还是不喜欢。这个设计背后的整个想法很糟糕。
template <typename T>
struct Holder
{
public:
Holder(T const& v)
{
new (&m_v) T(v);
}
T const& get() const
{
return reinterpret_cast<T const&>(m_v);
}
T& get()
{
return reinterpret_cast<T&>(m_v);
}
~Holder()
{
std::cout << "~Holder() " << std::endl;
get().~T();
}
private:
char m_v[sizeof(T)];
};
这个类不再做与您的相同的事情,即它不能在std::vector<Holder>
中存储任意类型,而只能存储相同的类型(std::vector<Holder<Foo>>
)。注释太小,无法包含此代码,我想为您正在使用的内容显示一个更好看的语法;)。
话虽如此,你能做你想做的事情的唯一方法是添加第二层用于参考计数。也就是说,用类似于shared_ptr
的东西替换void* _v
,但当计数为零时,它不会调用delete,而是调用deleter(因此应该存储在这个新类中)。事实上,您的类看起来基本上像这个新类,只是您应该使它不可复制并提供引用计数(即通过boost::intrusive_ptr
)。那么Holder
可以是可复制的封装器。
可能这行代码:delete reinterpret_cast<T*>(_v);
没有达到我的预期
不完全是。您的类型可能使用默认的复制ctor;这将复制您的数据指针_v
和您的deleter。因此,当两个对象都进行析构函数时,两个deleter都会触发,导致数据被删除两次。(附带说明--不应该以_
开头命名变量;这些标识符是为实现保留的)。
以下是正确执行类型擦除所需的步骤,假设我没有错误。更好的方法是坚持使用boost::any。
#include <utility>
struct EmptyType {}; // Thrown if unexpectedly empty
struct InvalidType {}; // Thrown if Holder(T) but get<U>.
struct Holder
{
Holder()
: data_()
, deleter_(e_deleter)
, copier_(e_copier)
, typetag_()
{
}
template<typename T>
Holder(const T& t)
: data_(erase_cast(new T))
, deleter_(deleter<T>)
// Need to explicitly carry T's copy behavior
// because Holder's default copy ctor isn't going to
, copier_(copier<T>)
// You need some way to protect against getting
// an Orange out of a Holder that holds an Apple.
, typetag_(id<T>())
{
}
Holder(const Holder& rhs)
: data_(rhs.copy())
, deleter_(rhs.deleter_)
, copier_(rhs.copier_)
, typetag_(rhs.typetag_)
{
}
template<typename T>
T get()
{
if (!data_) throw EmptyType();
T rv(fetch<T>());
return rv;
}
Holder(Holder&& rhs)
: data_()
, copier_(rhs.copier_)
, deleter_(rhs.deleter_)
, typetag_(rhs.typetag_)
{
std::swap(data_, rhs.data_);
}
~Holder()
{
destroy();
}
private:
// Reinterpret_cast wrappers labeled semantically
template<typename T>
static void* erase_cast(T* t) { return reinterpret_cast<void*>(t); }
template<typename T>
static T* unerase_cast(void* t) { return reinterpret_cast<T*>(t); }
// Return a data copy
void* copy() const { return copier_(data_); }
// Return const reference to data
template<typename T>
const T& fetch() {
if (typetag_!=id<T>()) throw InvalidType();
return *unerase_cast<T>(data_);
}
// Destroy data
void destroy() { deleter_(data_); data_=0; }
// ==== Type erased copy semantics ===
void*(*copier_)(void*);
template<typename T>
static void* copier(void* v) {
return erase_cast<T>(new T(*unerase_cast<T>(v)));
}
static void* e_copier(void*) { return 0; }
// ==== Type erased delete semantics ===
void(*deleter_)(void*);
template<typename T>
static void deleter(void* v) {
delete unerase_cast<T>(v);
}
static void e_deleter(void*) {}
// ==== Type protection using tagging (could also use typeid)
static int makenewid() { static int i=0; return i++;}
template<typename T>
static int id() { static int i=makenewid(); return i; }
// Type erased data
void* data_;
// Type erased tag
int typetag_;
};
这里有一些测试/演示代码:
#include <iostream>
#include <vector>
#define FAIL() std::cout << "Fail" << std::endl; return 1
int foos=0;
struct Foo { Foo(){++foos;} Foo(const Foo&){++foos;} ~Foo(){--foos;} };
int bars=0;
struct Bar { Bar(){++bars;} Bar(const Bar&){++bars;} ~Bar(){--bars;} };
int main() {
{
std::vector<Holder> v;
Foo fx,fy,fz; Bar ba,bb;
v.push_back(fx); v.push_back(fy); v.push_back(fz);
v.push_back(ba); v.push_back(ba); v.push_back(bb);
v.push_back(Holder());
try {
Foo y = v[2].get<Foo>();
}
catch (EmptyType&) { FAIL(); }
catch (InvalidType&) { FAIL(); }
try {
Foo y = v[4].get<Foo>();
FAIL();
}
catch (EmptyType&) { FAIL(); }
catch (InvalidType&) { }
try {
Foo y = v[6].get<Foo>();
FAIL();
}
catch (EmptyType&) { }
catch (InvalidType&) { FAIL(); }
}
if (foos||bars) { FAIL(); }
std::cout << "Pass" << std::endl;
}
测试结果:
$ ./a.exe
Pass
- 处理模板函数包装中的void返回
- 指针中的Void*有什么用
- 代码行 1 中的"(void)"有什么用?
- 如何通过c 中的void*的字节迭代
- 函数调用中的 void 指针在 C++ 与 C 中
- 将数据读入结构中的 void* 变量
- 如何在 c++ 结构中的 void* 变量中调用存储函数(回调)
- 'auto f(params) -> decltype(..., void())' 中的 'void()' 是做什么的?
- 删除visualstudio标题中的void指针
- 正在修改cpp中的void指针
- 函数参数c++中的void**指针
- C++如何修复旧代码中的 void* 算术
- c++ 结构中的 void 不会更改一个结构值
- C++ 中的 void* 与向量<uint8_t>
- 无法在c++中的void中分配指针
- 使用类型擦除删除C++11中的void*
- 避免c++中的void*
- 删除c++中的void*指针
- decltype(void())中的void()到底是什么意思?
- Qt5中的void QSqlQuery::clear()和void QSqlQuery::finish()之间有什么区别