没有什么能阻止您void*
在 C++ 中使用 C 构造,例如 a 。然而,它通常不受欢迎,因为如果代码以非预期的方式使用,或者所述操作的后果没有被完整记录,它可能会为各种错误打开大门。
话虽如此,您实际上是在要求将 a 包装void*
在一个类中,然后该类可以在 a 中使用std::vector
,然后再访问。
这是我前段时间编写的框架中的一些代码,以达到类似的效果:
generic_ptr.hpp
#include <exception>
#include <typeinfo>
#include <map>
namespace so {
std::map<std::size_t, std::size_t> type_sizes;
template < typename T >
std::size_t type_id()
{
static char tid;
std::size_t sz = reinterpret_cast<std::size_t>(&tid);
so::type_sizes[sz] = sizeof(T);
return sz;
}
template < typename T >
inline std::size_t type_id(const T& t)
{
return so::type_id<T>();
}
template < typename T >
inline std::size_t type_id(const T *const t)
{
return so::type_id<T>();
}
template < typename T, typename C >
inline bool type_of()
{
return so::type_id<T>() == so::type_id<C>();
}
template < typename T, typename C >
inline bool type_of(const C& c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const C *const c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const T& t, const C& c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const T *const t, const C *const c)
{
return so::type_of<T, C>();
}
class generic_ptr
{
public:
generic_ptr() : m_ptr(0), m_id(0) { }
template < typename T >
generic_ptr(T *const obj) :
m_ptr(obj), m_id(so::type_id<T>())
{
}
generic_ptr(const generic_ptr &o) :
m_ptr(o.m_ptr), m_id(o.m_id)
{
}
~generic_ptr()
{
this->invalidate();
}
static generic_ptr null()
{
return generic_ptr();
}
void invalidate()
{
this->m_ptr = 0;
this->m_id = 0;
}
template < typename T >
bool is_type() const
{
return this->m_id == so::type_id<T>();
}
template < typename T >
void gc()
{
delete ((T*)this->m_ptr);
this->invalidate();
}
bool valid() const
{
return (this->m_ptr != 0);
}
operator bool() const
{
return (this->m_ptr != 0);
}
bool operator!() const
{
return (!operator bool());
}
generic_ptr& operator=(const generic_ptr &o)
{
this->m_ptr = o.m_ptr;
this->m_id = o.m_id;
return *this;
}
template < typename T >
const generic_ptr& operator=(T *const obj)
{
this->m_ptr = obj;
this->m_id = so::type_id<T>();
return *this;
}
template < typename T >
operator T *const() const
{
if (this->m_id != so::type_id<T>()) {
throw std::bad_cast();
}
return static_cast<T *const>(
const_cast<void *const>(this->m_ptr)
);
}
template < typename T >
operator const T *const() const
{
if ((this->m_id != so::type_id<T>()) && (this->m_id != so::type_id<const T>())) {
throw std::bad_cast();
}
return static_cast<const T *const>(this->m_ptr);
}
operator void *const() const
{
return const_cast<void*>(this->m_ptr);
}
operator const void *const() const
{
return this->m_ptr;
}
bool operator==(const generic_ptr& o) const
{
return (this->m_ptr == o.m_ptr && this->m_id == o.m_id);
}
bool operator!=(const generic_ptr& o) const
{
return !(*this == o);
}
std::size_t hash() const
{
return this->m_id;
}
private:
const void* m_ptr;
std::size_t m_id;
};
}
然后使用它:
主文件
#include <iostream>
#include <vector>
#include "generic_ptr.hpp"
class MyClass {
public:
MyClass() : m_val1(10), m_val2(20), m_val3(10), m_val4(2) {}
MyClass(int a, int b, int c, int d) : m_val1(a), m_val2(b), m_val3(c), m_val4(d) {}
friend std::ostream& operator<<(std::ostream& os, const MyClass& mc)
{
os << mc.m_val1 << " + " <<
mc.m_val2 << " + " <<
mc.m_val3 << " + " <<
mc.m_val4 << " = " <<
(mc.m_val1 + mc.m_val2 + mc.m_val3 + mc.m_val4);
return os;
}
private:
int m_val1;
int m_val2;
int m_val3;
int m_val4;
};
template < typename T >
void print(so::generic_ptr& g_ptr)
{
std::cout << "sizeof = " << so::type_sizes[g_ptr.hash()]
<< ", val = " << *((T*)g_ptr) << std::endl;
}
template < typename T >
void cleanup(so::generic_ptr& g_ptr)
{
delete ((T*)g_ptr);
}
int main(int argc, char* argv[])
{
std::vector<so::generic_ptr> items;
items.push_back(new int(10));
items.push_back(new double(3.14159));
items.push_back(new MyClass());
items.push_back(new char(65));
items.push_back(new MyClass(42,-42,65536,9999));
items.push_back(new int(999));
for (auto i : items) {
if (i.is_type<int>()) { print<int>(i); }
else if (i.is_type<char>()) { print<char>(i); }
else if (i.is_type<double>()) { print<double>(i); }
else if (i.is_type<MyClass>()) { print<MyClass>(i); }
}
int* i = (int*)items[0];
std::cout << "i = " << *i << std::endl;
*i = 500;
std::cout << "i = " << *i << std::endl;
try {
double* d = (double*)items[0];
std::cout << "d = " << *d << std::endl;
} catch (std::bad_cast& ex) {
std::cout << ex.what() << std::endl;
}
for (auto i : items) {
if (i.is_type<int>()) {
print<int>(i);
cleanup<int>(i);
} else if (i.is_type<char>()) {
print<char>(i);
cleanup<char>(i);
} else if (i.is_type<double>()) {
print<double>(i);
cleanup<double>(i);
} else if (i.is_type<MyClass>()) {
print<MyClass>(i);
cleanup<MyClass>(i);
}
}
return 0;
}
当然,您仍然必须知道类型并跟踪内存,但您可以修改代码来处理它;使用运算符重载,您不需要recover
这种方式的函数,您可以像在print
代码中一样进行强制转换:*((T*)g_ptr)
,并且可以通过原始指针访问它,就像在最后一条for..each
语句之前一样:
int* i = (int*)items[0];
*i = 500;
print<int>(items[0]);
如果您尝试在无效类型之间进行转换,该类还内置了无效类型转换:
try {
double* d = (double*)items[0];
// items[0] is an int, so this will throw a std::bad_cast
std::cout << "d = " << *d << std::endl;
} catch (std::bad_cast& ex) {
std::cout << ex.what() << std::endl;
}
老实说,在这种情况下,便利性可能胜过安全性,因此,如果您需要一组不一致的类型或无法使用基类定义的类型,您可能需要重新考虑您要在C++ 方式。
我希望这可以帮助您弄清楚。