(Под стиранием типа я подразумеваю скрытие некоторой или всей информации о типе, относящейся к классу, что-то вроде Boost. Any .)
Я хочу овладеть методами стирания шрифтов, а также поделиться теми, о которых я знаю. Я надеюсь найти какую-то безумную технику, о которой кто-то подумал в самый темный час. :)
Первый, наиболее очевидный и распространенный подход, который я знаю, - это виртуальные функции. Просто скройте реализацию вашего класса внутри иерархии классов на основе интерфейса. Многие библиотеки Boost делают это, например Boost.Any делает это, чтобы скрыть ваш тип, а Boost.Shared_ptr делает это, чтобы скрыть механизм (де) распределения.
Затем есть вариант с указателями функций на шаблонные функции, при сохранении фактического объекта в указателе void *
, как, например, Boost.Function , чтобы скрыть реальный тип объекта функтор. Примеры реализации можно найти в конце вопроса.
Итак, на мой актуальный вопрос:
Какие еще техники стирания типа вам известны? Пожалуйста, предоставьте им, если возможно, пример кода, варианты использования, ваш опыт работы с ними и, возможно, ссылки для дальнейшего чтения.
Изменить
(Поскольку я не был уверен, что добавлю это в качестве ответа или просто отредактируйте вопрос, я просто сделаю более безопасный.)
Другой хороший способ скрыть реальный тип чего-то без виртуальных функций или void *
возни, GMan применяет здесь , имеющий отношение к my вопрос о том, как именно это работает.
Пример кода:
#include
#include
// NOTE: The class name indicates the underlying type erasure technique
// this behaves like the Boost.Any type w.r.t. implementation details
class Any_Virtual{
struct holder_base{
virtual ~holder_base(){}
virtual holder_base* clone() const = 0;
};
template
struct holder : holder_base{
holder()
: held_()
{}
holder(T const& t)
: held_(t)
{}
virtual ~holder(){
}
virtual holder_base* clone() const {
return new holder(*this);
}
T held_;
};
public:
Any_Virtual()
: storage_(0)
{}
Any_Virtual(Any_Virtual const& other)
: storage_(other.storage_->clone())
{}
template
Any_Virtual(T const& t)
: storage_(new holder(t))
{}
~Any_Virtual(){
Clear();
}
Any_Virtual& operator=(Any_Virtual const& other){
Clear();
storage_ = other.storage_->clone();
return *this;
}
template
Any_Virtual& operator=(T const& t){
Clear();
storage_ = new holder(t);
return *this;
}
void Clear(){
if(storage_)
delete storage_;
}
template
T& As(){
return static_cast*>(storage_)->held_;
}
private:
holder_base* storage_;
};
// the following demonstrates the use of void pointers
// and function pointers to templated operate functions
// to safely hide the type
enum Operation{
CopyTag,
DeleteTag
};
template
void Operate(void*const& in, void*& out, Operation op){
switch(op){
case CopyTag:
out = new T(*static_cast(in));
return;
case DeleteTag:
delete static_cast(out);
}
}
class Any_VoidPtr{
public:
Any_VoidPtr()
: object_(0)
, operate_(0)
{}
Any_VoidPtr(Any_VoidPtr const& other)
: object_(0)
, operate_(other.operate_)
{
if(other.object_)
operate_(other.object_, object_, CopyTag);
}
template
Any_VoidPtr(T const& t)
: object_(new T(t))
, operate_(&Operate)
{}
~Any_VoidPtr(){
Clear();
}
Any_VoidPtr& operator=(Any_VoidPtr const& other){
Clear();
operate_ = other.operate_;
operate_(other.object_, object_, CopyTag);
return *this;
}
template
Any_VoidPtr& operator=(T const& t){
Clear();
object_ = new T(t);
operate_ = &Operate;
return *this;
}
void Clear(){
if(object_)
operate_(0,object_,DeleteTag);
object_ = 0;
}
template
T& As(){
return *static_cast(object_);
}
private:
typedef void (*OperateFunc)(void*const&,void*&,Operation);
void* object_;
OperateFunc operate_;
};
int main(){
Any_Virtual a = 6;
std::cout << a.As() << std::endl;
a = std::string("oh hi!");
std::cout << a.As() << std::endl;
Any_Virtual av2 = a;
Any_VoidPtr a2 = 42;
std::cout << a2.As() << std::endl;
Any_VoidPtr a3 = a.As();
a2 = a3;
a2.As() += " - again!";
std::cout << "a2: " << a2.As() << std::endl;
std::cout << "a3: " << a3.As() << std::endl;
a3 = a;
a3.As().As() += " - and yet again!!";
std::cout << "a: " << a.As() << std::endl;
std::cout << "a3->a: " << a3.As().As() << std::endl;
std::cin.get();
}