Bir kök nesnesine sahip olmak, yapabileceklerinizi ve derleyicinin yapabileceklerini çok fazla ödeme yapmadan sınırlar.
Ortak bir kök sınıf kapları-of-the şey yaratmak ve bir ile ne ayıklamak mümkün kılar dynamic_cast
, ancak kapları-of-the şey daha sonra benzer bir şey gerekiyorsa boost::any
bunu yapabilir olmadan ortak bir kök sınıfına. Ve boost::any
ayrıca ilkeller destekler - hatta küçük tampon optimizasyonu desteklemek ve Java dilinde neredeyse "Kutusuz" bırakabilirsiniz.
C ++, değer türlerini destekler ve büyür. Hem değişmez, hem de programcı yazılı değer türleri. C ++ kapları, değer türlerini verimli bir şekilde depolar, sıralar, karma, tüketir ve üretir.
Kalıtım, özellikle monolitik kalıtımın türü Java stil temel sınıfları, serbest mağaza tabanlı "pointer" veya "başvuru" türlerini gerektirir. İşleç / işaretçi / verilere referans, sınıfın arabirimine bir işaretçi tutar ve polimorfik olarak başka bir şeyi temsil edebilir.
Bu, bazı durumlarda faydalı olsa da, bir kez "ortak temel sınıf" ile kalıpla evlendikten sonra, kod tabanınızın tamamını, yararlı olmasa bile, bu kalıpların maliyetine ve bagajına kilitlediniz.
Neredeyse her zaman bir tür hakkında, arayan sitede veya onu kullanan kodda "bu bir nesnedir" den daha fazlasını bilirsiniz.
İşlev basitse, işlevi bir şablon olarak yazmak, arayan sitedeki bilgilerin atılmadığı durumlarda ördek tipi derleme zamanı tabanlı polimorfizm sağlar. İşlev daha karmaşıksa, yapmak istediğiniz tipteki (örneğin seri hale getirme ve seri hale getirme) tek tip işlemlerin (derleme zamanında) tüketilebilecek (çalışma zamanında) oluşturulabileceği ve saklanabileceği tür silme işlemi yapılabilir. kodu farklı bir çeviri biriminde
Her şeyin serileştirilebilir olmasını istediğiniz bir kütüphaneniz olduğunu varsayalım. Bir yaklaşım temel bir sınıfa sahip olmaktır:
struct serialization_friendly {
virtual void write_to( my_buffer* ) const = 0;
virtual void read_from( my_buffer const* ) = 0;
virtual ~serialization_friendly() {}
};
Şimdi yazdığınız her kod parçası olabilir serialization_friendly
.
void serialize( my_buffer* b, serialization_friendly const* x ) {
if (x) x->write_to(b);
}
Bir hariç std::vector
, şimdi her kabı yazman gerekiyor. Ve o bignum kütüphanesinden aldığın tamsayılar değil. Ve yazdığınız türden değil serileştirme gerekli olduğunu düşünmediniz. Ve bir tuple
, veya bir int
veya bir double
veya a std::ptrdiff_t
.
Başka bir yaklaşım benimsiyoruz:
void write_to( my_buffer* b, int x ) {
b->write_integer(x);
}
template<class T,
class=std::enable_if_t< void_t<
std::declval<T const*>()->write_to( std::declval<my_buffer*>()
> >
>
void write_to( my_buffer* b, T const* x ) {
if (x) x->write_to(b);
}
template<class T>
void serialize( my_buffer* b, T const& t ) {
write_to( b, t );
}
görünüşe göre hiçbir şey yapmamaktan ibarettir. Artık , bir türün ad alanındaki boş bir işlevi veya türdeki bir yöntemi write_to
geçersiz kılarak genişletebiliriz write_to
.
Hatta bir tür silme kodu yazabiliriz:
namespace details {
struct can_serialize_pimpl {
virtual void write_to( my_buffer* ) const = 0;
virtual void read_from( my_buffer const* ) = 0;
virtual ~can_serialize_pimpl() {}
};
}
struct can_serialize {
void write_to( my_buffer* b ) const { pImpl->write_to(b); }
void read_from( my_buffer const* b ) { pImpl->read_from(b); }
std::unique_ptr<details::can_serialize_pimpl> pImpl;
template<class T> can_serialize(T&&);
};
namespace details {
template<class T>
struct can_serialize : can_serialize_pimpl {
std::decay_t<T>* t;
void write_to( my_buffer*b ) const final override {
serialize( b, std::forward<T>(*t) );
}
void read_from( my_buffer const* ) final override {
deserialize( b, std::forward<T>(*t) );
}
can_serialize(T&& in):t(&in) {}
};
}
template<class T> can_serialize::can_serialize<T>(T&&t):pImpl(
std::make_unique<details::can_serialize<T>>( std::forward<T>(t) );
) {}
ve şimdi keyfi bir tür alabilir ve daha sonra sanal bir arabirim aracılığıyla can_serialize
çağırmanıza izin veren bir arabirime otomatik olarak yerleştirebiliriz serialize
.
Yani:
void writer_thingy( can_serialize s );
yerine seri hale gelebilecek her şeyi alan bir işlevdir.
void writer_thingy( serialization_friendly const* s );
ve birincisi, ikincisinin aksine int
, std::vector<std::vector<Bob>>
otomatik olarak idare edebilir .
Bunu yazmak çok fazla zaman almadı, özellikle de bu tür bir şey, nadiren yapmak istediğiniz bir şey olduğu için, ancak bir baz türüne gerek duymadan seri hale getirilebilir bir şeyi işleme yeteneği kazandık.
Dahası, şimdi std::vector<T>
sadece geçersiz kılarak birinci sınıf bir vatandaş olarak seri hale getirilebilir hale getirebiliriz write_to( my_buffer*, std::vector<T> const& )
- bu aşırı yükle, a'ya geçirilebilir can_serialize
ve std::vector
elde edilenlerin seri hale getirilebilirliği bir vtable'da saklanır ve erişilir .write_to
.
Kısacası, C ++, gerektiğinde zorunlu bir kalıtım hiyerarşisinin bedelini ödemek zorunda kalmadan, anında tek bir temel sınıfın avantajlarını uygulayabileceğiniz kadar güçlüdür. Ve tek tabanın (sahte veya sahte) gerekli olduğu zamanlar oldukça nadirdir.
Türler aslında onların kimlikleri olduğunda ve ne olduklarını bildiğiniz zaman, optimizasyon fırsatları boldur. Veriler yerel olarak ve bitişik olarak depolanır (ki bu modern işlemcilerde önbellek dostu olması için son derece önemlidir), derleyiciler verilen bir işlemin ne yaptığını kolayca anlayabilir (üzerinde bir opak sanal yöntem göstergesine sahip olmak yerine, üzerinde bilinmeyen bir koda neden olmak üzere) diğer taraf) talimatların en iyi şekilde yeniden sıralanmasını sağlar ve daha az sayıda yuvarlak mandal yuvarlak deliklere açılır.