cpp
#include <iostream>
#include <typeinfo>
#include <cassert>
#include <unistd.h>
class Any
{
private:
class holder
{
public:
virtual ~holder() {}
virtual const std::type_info &type() = 0;
virtual holder *clone() = 0;
};
template <class T>
class placeholder : public holder
{
public:
placeholder(const T &val) : _val(val) {}
// 获取子类对象保存的数据类型
virtual const std::type_info &type()
{
return typeid(T);
}
// 针对当前对象,克隆出一个新子类对象
virtual holder *clone()
{
return new placeholder(_val);
}
public:
T _val;
};
holder *_content;
public:
Any() : _content(nullptr) {}
template <class T>
Any(const T &val) : _content(new placeholder<T>(val)) {}
Any(const Any &other) : _content(other._content ? other._content->clone() : nullptr) {}
~Any() { delete _content; }
Any &swap(Any &other)
{
std::swap(_content, other._content);
return *this;
}
template <class T>
T *get()
{
if (_content == nullptr)
return nullptr;
assert(typeid(T) == _content->type());
auto p = static_cast<placeholder<T> *>(_content);
return &(p->_val);
}
template <class T>
Any &operator=(const T &val)
{
Any(val).swap(*this);
return *this;
}
Any &operator=(const Any &other)
{
Any(other).swap(*this);
return *this;
}
};
int main()
{
Any a(100);
int *p = a.get<int>();
std::cout << *p << std::endl;
Any b = std::string("hello any");
std::string *s = b.get<std::string>();
std::cout << *s << std::endl;
Any c;
c = a;
std::cout << *c.get<int>() << std::endl;
// 空对象测试
Any empty;
auto pe = empty.get<int>();
if (pe == nullptr)
{
std::cout << "empty object, get return nullptr\n";
}
return 0;
}
在C语⾔中,通⽤类型可以使⽤void*来管理,但是在C++中,boost库和C++17给我们提供了⼀个通⽤类型any来灵活使⽤,如果考虑增加代码的移植性,尽量减少第三⽅库的依赖,则可以使⽤C++17特性中的any,或者⾃⼰来实现。⽽这个any通⽤类型类的实现其实并不复杂,以下是简单的部分实现。
Any是经典依靠类型擦除 (Type Erasure)实现,依靠"父类虚函数 + 子类继承",可以在同一个容器里保存任意类型对象(类似std::any底层原理)。
holder:抽象基类
cpp
class holder{
public:
virtual ~holder(){}
virtual const std::type_info& type() const = 0;
virtual holder* clone() const = 0;
};
纯虚函数 type() 和 clone(),所以holder是抽象类,不能实例化,只能用来做基类。
虚析构函数 virtual ~holder():继承体系必须要有 ,否则delete _content的时候只会调用父类析构,子类placeholder<T>的内存不会释放,造成内存泄漏。两个虚函数是接口:
type():返回保存值的真实类型信息clone():拷贝构造时,新建一个同类型的 placeholder 对象
placeholder<T>:模板子类,public 继承 holder
cpp
template<class T>
class placeholder : public holder{
public:
placeholder(const T& val):_val(val){}
virtual const std::type_info& type() const override{
return typeid(T);
}
virtual holder* clone() const override{
return new placeholder(_val);
}
T _val; // 真正存储数据的成员
};
继承holder,重写 override 两个虚函数。
每一个不同的T,都会实例化一个独立的placeholder<T>类。placeholder<int>、
placeholder<std::string>是两个完全不同的类,只是都继承自 holder。
_val:保存真实的数据。
Any 类成员 holder* _content;
关键点:父类指针可以指向子类对象 holder*是基类指针,它可以接收任意placeholder<T>子类对象的地址。
_content = new placeholder<int>(100);// _content是holder*,但它实际指向placeholder<int>对象
继承 + 多态如何实现类型擦除
构造 Any a(100)
cpp
template<class T>
Any(const T&val):_content(new placeholder<T>(val)){}
new placeholder<int>(100) 创建子类对象,返回placeholder<int>*,隐式向上转型为 holder*,赋值给_content。
向上转型:子类指针 → 父类指针,安全,C++ 继承原生支持。
调用虚函数(多态)
虽然_content类型是holder*,但是运行时看它实际指向的对象类型:
如果指向placeholder<int>,调用placeholder<int>::type()
如果指向placeholder<string>,调用placeholder<string>::type()
clone 拷贝
cpp
virtual holder* clone() const override{
return new placeholder(_val);
}
拷贝 Any 的时候,调用_content->clone(),虚函数会调用对应 placeholder 的 clone,新建一个同类型对象,返回placeholder<T>*,同样向上转型为holder*。
这就是深拷贝的核心,保证Any b = a不会两个 Any 共用同一个_content。
get<T>() 函数
cpp
template<class T>
T* get()
{
if(_content == nullptr)
return nullptr;
assert(typeid(T)==_content->type());
auto p = static_cast<placeholder<T>*>(_content);
return &(p->_val);
}
_content是holder*(父类指针),我们知道它实际指向 placeholder<T>子类对象 ,于是用static_cast做向下转型:父类指针 → 子类指针。