(Dengan tipe erasure, maksud saya menyembunyikan beberapa atau semua tipe informasi mengenai kelas, agak seperti Boost . Setiap .)
Saya ingin mengetahui teknik-teknik penghapusan tipe, sambil juga membagikan yang, yang saya tahu. Harapan saya agak untuk menemukan beberapa teknik gila yang dipikirkan seseorang di jam tergelapnya. :)
Yang pertama dan paling jelas, dan pendekatan yang biasa diambil, yang saya tahu, adalah fungsi virtual. Sembunyikan saja implementasi kelas Anda di dalam hierarki kelas berbasis antarmuka. Banyak pustaka Boost melakukan ini, misalnya Boost.Any melakukan ini untuk menyembunyikan tipe Anda dan Boost.Shared_ptr melakukan ini untuk menyembunyikan mekanik alokasi (de).
Lalu ada opsi dengan fungsi pointer ke fungsi templated, sambil memegang objek yang sebenarnya dalam void*
pointer, seperti Boost. Fungsi yang dilakukannya untuk menyembunyikan tipe nyata dari functor. Contoh implementasi dapat ditemukan di akhir pertanyaan.
Jadi, untuk pertanyaan saya yang sebenarnya:
Apa teknik penghapusan tipe lain yang Anda ketahui? Harap berikan kepada mereka, jika mungkin, dengan kode contoh, gunakan case, pengalaman Anda dengan mereka dan mungkin tautan untuk membaca lebih lanjut.
Sunting
(Karena saya tidak yakin apakah akan menambahkan ini sebagai jawaban, atau hanya mengedit pertanyaan, saya hanya akan melakukan yang lebih aman.)
Teknik lain yang bagus untuk menyembunyikan jenis sesuatu yang sebenarnya tanpa fungsi virtual atau void*
mengutak - atik, adalah satu GMan mempekerjakan di sini , dengan relevansi dengan pertanyaan saya tentang bagaimana tepatnya ini bekerja.
Kode contoh:
#include <iostream>
#include <string>
// 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<class T>
struct holder : holder_base{
holder()
: held_()
{}
holder(T const& t)
: held_(t)
{}
virtual ~holder(){
}
virtual holder_base* clone() const {
return new holder<T>(*this);
}
T held_;
};
public:
Any_Virtual()
: storage_(0)
{}
Any_Virtual(Any_Virtual const& other)
: storage_(other.storage_->clone())
{}
template<class T>
Any_Virtual(T const& t)
: storage_(new holder<T>(t))
{}
~Any_Virtual(){
Clear();
}
Any_Virtual& operator=(Any_Virtual const& other){
Clear();
storage_ = other.storage_->clone();
return *this;
}
template<class T>
Any_Virtual& operator=(T const& t){
Clear();
storage_ = new holder<T>(t);
return *this;
}
void Clear(){
if(storage_)
delete storage_;
}
template<class T>
T& As(){
return static_cast<holder<T>*>(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<class T>
void Operate(void*const& in, void*& out, Operation op){
switch(op){
case CopyTag:
out = new T(*static_cast<T*>(in));
return;
case DeleteTag:
delete static_cast<T*>(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<class T>
Any_VoidPtr(T const& t)
: object_(new T(t))
, operate_(&Operate<T>)
{}
~Any_VoidPtr(){
Clear();
}
Any_VoidPtr& operator=(Any_VoidPtr const& other){
Clear();
operate_ = other.operate_;
operate_(other.object_, object_, CopyTag);
return *this;
}
template<class T>
Any_VoidPtr& operator=(T const& t){
Clear();
object_ = new T(t);
operate_ = &Operate<T>;
return *this;
}
void Clear(){
if(object_)
operate_(0,object_,DeleteTag);
object_ = 0;
}
template<class T>
T& As(){
return *static_cast<T*>(object_);
}
private:
typedef void (*OperateFunc)(void*const&,void*&,Operation);
void* object_;
OperateFunc operate_;
};
int main(){
Any_Virtual a = 6;
std::cout << a.As<int>() << std::endl;
a = std::string("oh hi!");
std::cout << a.As<std::string>() << std::endl;
Any_Virtual av2 = a;
Any_VoidPtr a2 = 42;
std::cout << a2.As<int>() << std::endl;
Any_VoidPtr a3 = a.As<std::string>();
a2 = a3;
a2.As<std::string>() += " - again!";
std::cout << "a2: " << a2.As<std::string>() << std::endl;
std::cout << "a3: " << a3.As<std::string>() << std::endl;
a3 = a;
a3.As<Any_Virtual>().As<std::string>() += " - and yet again!!";
std::cout << "a: " << a.As<std::string>() << std::endl;
std::cout << "a3->a: " << a3.As<Any_Virtual>().As<std::string>() << std::endl;
std::cin.get();
}
shared_ptr
tidak mencerminkan ini, itu akan selalu sama, shared_ptr<int>
misalnya, tidak seperti wadah standar.
As
fungsi seperti itu tidak akan diimplementasikan seperti itu. Seperti saya katakan, tidak berarti aman digunakan! :)
function
, shared_ptr
, any
, Dll? Mereka semua menggunakan tipe erasure untuk kenyamanan pengguna yang manis.