(Con la cancellazione del tipo, intendo nascondere alcune o tutte le informazioni sul tipo relative a una classe, un po 'come Boost . Qualunque .)
Voglio ottenere delle tecniche di cancellazione del tipo, condividendo anche quelle che conosco. La mia speranza è di trovare una tecnica folle a cui qualcuno abbia pensato nella sua ora più buia. :)
Il primo e più ovvio e comunemente adottato approccio, che conosco, sono funzioni virtuali. Nascondi l'implementazione della tua classe all'interno di una gerarchia di classi basata su interfaccia. Molte librerie di Boost fanno questo, per esempio Boost. Qualcuno lo fa per nascondere il tuo tipo e Boost.Shared_ptr lo fa per nascondere il meccanico (de) allocazione.
Quindi c'è l'opzione con i puntatori di funzione alle funzioni di modello, mentre si tiene l'oggetto reale in un void*
puntatore, come Boost.Function fa per nascondere il tipo reale del funzione. Implementazioni di esempio sono disponibili alla fine della domanda.
Quindi, per la mia vera domanda:
quali altri tipi di tecniche di cancellazione conosci? Fornisci loro, se possibile, un codice di esempio, casi d'uso, la tua esperienza con essi e forse collegamenti per ulteriori letture.
Modifica
(Dal momento che non ero sicuro di aggiungere questo come risposta, o semplicemente modificare la domanda, farò solo la più sicura.)
Un'altra bella tecnica per nascondere il tipo reale di qualcosa senza funzioni virtuali o void*
giocherellare, è la un GMan impiega qui , in relazione alla mia domanda su come funziona esattamente.
Codice di esempio:
#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
non riflette questo, sarà sempre lo stesso, shared_ptr<int>
ad esempio, a differenza del contenitore standard.
As
o più funzioni di questo tipo non sarebbero implementate in questo modo. Come ho detto, non è sicuro da usare! :)
function
, shared_ptr
, any
, Ecc? Tutti utilizzano la cancellazione del tipo per una dolce comodità dell'utente.