(Với kiểu xóa, ý tôi là ẩn một số hoặc tất cả thông tin loại liên quan đến một lớp, hơi giống như Boost.Any .)
Tôi muốn nắm giữ các kỹ thuật xóa kiểu, đồng thời chia sẻ những thông tin mà tôi biết. Hy vọng của tôi là tìm thấy một số kỹ thuật điên rồ mà ai đó nghĩ đến trong giờ đen tối nhất của anh ấy / cô ấy. :)
Cách tiếp cận đầu tiên và rõ ràng nhất và thường được thực hiện, mà tôi biết, là các hàm ảo. Chỉ cần ẩn việc thực hiện lớp của bạn bên trong một hệ thống phân cấp lớp dựa trên giao diện. Nhiều thư viện Boost làm điều này, ví dụ Boost.Any làm điều này để ẩn loại của bạn và Boost.Shared_ptr làm điều này để ẩn cơ chế phân bổ (de).
Sau đó, có tùy chọn với các con trỏ hàm cho các hàm templated, trong khi giữ đối tượng thực tế trong một void*
con trỏ, giống như Boost.Factor không để ẩn kiểu thực của hàm functor. Ví dụ triển khai có thể được tìm thấy ở cuối câu hỏi.
Vì vậy, đối với câu hỏi thực tế của tôi:
Những kỹ thuật tẩy xóa loại nào khác mà bạn biết? Vui lòng cung cấp cho họ, nếu có thể, với một mã ví dụ, các trường hợp sử dụng, kinh nghiệm của bạn với họ và có thể liên kết để đọc thêm.
Chỉnh sửa
(Vì tôi không chắc chắn sẽ thêm câu này dưới dạng câu trả lời hoặc chỉ chỉnh sửa câu hỏi, tôi sẽ chỉ thực hiện một cách an toàn hơn.)
Một kỹ thuật hay khác để ẩn loại thực tế của một thứ không có chức năng ảo hoặc void*
nghịch ngợm, là một GMan sử dụng ở đây , liên quan đến câu hỏi của tôi về cách chính xác điều này hoạt động.
Mã ví dụ:
#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
không phản ánh điều này, nó sẽ luôn giống nhau, shared_ptr<int>
ví dụ, không giống như container tiêu chuẩn.
As
chức năng (s) như vậy sẽ không được thực hiện theo cách đó. Như tôi đã nói, không có nghĩa là an toàn để sử dụng! :)
function
, shared_ptr
, any
, Vv? Tất cả đều sử dụng loại tẩy xóa để thuận tiện cho người dùng ngọt ngào.