Niçin tüm nesnelerin temeli C ++ 'ta önerilmiyor?


76

Stroustrup, "Derhal tüm sınıflarınız için benzersiz bir üs icat etme (Nesne sınıfı). Genellikle, çoğu / çoğu sınıf için onsuz daha iyisini yapabilirsiniz." Diyor. (C ++ Programlama Dili Dördüncü Basım, Bölüm 1.3.4)

Neden her şey için bir temel sınıf genellikle kötü bir fikirdir ve ne zaman bir tane yaratmanın anlamı vardır?


16
Çünkü C ++ Java değil ... Ve zorlamak zorunda kalmamalısın.
AK_

10
Yığın Taşması Hakkında Sorulan Sorular: Neden C ++ 'ta temel sınıf yok?

26
Ayrıca, "öncelikli görüşe dayalı" için yakın oylara katılmıyorum. Cevaplar hem bu soruya hem de bağlantılı SO sorusuna cevap verdiğinden, bunun için açıklanabilecek çok özel nedenler var.

2
Çevik "ona ihtiyacın olmayacak" ilkesidir. Özel bir ihtiyacı zaten belirlemediyseniz, bunu yapmayın (yapana kadar).
Jool

3
@AK_: Yorumunuzda "kadar aptal" bir eksik var.
DeadMG

Yanıtlar:


75

Çünkü bu nesnenin işlevsellik için ne var? Java'da tüm Base sınıflarının bir toString, hashCode ve eşitliği ve monitor + koşul değişkeni vardır.

  • ToString sadece hata ayıklama için kullanışlıdır.

  • hashCode, yalnızca karma tabanlı bir koleksiyonda saklamak istiyorsanız kullanışlıdır (C ++'daki tercih, şablon param olarak kaba bir karma işlevini iletmek veya std::unordered_*tamamen ve bunun yerine std::vectordüz ve sıralanmamış listeleri kullanmaktan kaçınmaktır ).

  • temel nesnesi olmayan eşitlik derleme sırasında yardımcı olabilir, eğer aynı tipte değilse, eşit olamazlar. C ++ 'da bu bir derleme zamanı hatasıdır.

  • Monitör ve koşul değişkeni, durum bazında bir duruma açıkça dahil edilir.

Ancak, yapılması gereken daha çok şey varsa, o zaman bir kullanım durumu vardır.

Örneğin, QT'de QObject, iplik benzeşimi, ebeveyn-çocuk sahipliği hiyerarşisi ve sinyal slotları mekanizmasının temelini oluşturan kök sınıfı vardır. Ayrıca QObjects için işaretçiyi kullanmaya zorlar, ancak Qt'daki birçok Sınıf QObject'i devralmaz çünkü sinyal yuvasına (özellikle de bazı tanımların değer türleri) ihtiyaç duymazlar.


7
Java'nın bir temel sınıfa sahip olmasının temel nedenini söylemeyi unuttun: Jeneriklerden önce, toplama sınıflarının çalışması için temel sınıfın gerekli olması gerekir. Her şey (dahili depolama, parametreler, dönüş değerleri) yazılmıştır Object.
Aleksandr Dubinsky

1
@AleksandrDubinsky: Ve jenerikler sadece sözdizimsel şeker eklediler, cilaları değiştirmiyorlardı.
Deduplicator

4
Tartışmalıyım, karma kodu, eşitlik ve monitör desteği, Java'daki tasarım hatalarıdır. Bütün nesneleri kilitlemenin iyi bir fikir olduğunu kim düşündü ?!
usr

1
Evet, ama kimse istemiyor. En son ne zaman bir nesneyi kilitlemeniz gerekti ve bunu yapmak için ayrı bir kilit nesnesini başlatamadı. Çok nadir görülür ve her şeye bir yük getirir. Java'lılar, o zamanlar tüm nesnelerin kilit olduğu ve artık kullanımdan kaldırılan iplik güvenli koleksiyonlarının kanıtı olarak iplik güvenliği konusunda kötü bir anlayışa sahipti. İplik güvenliği, nesne başına değil, küresel bir özelliktir.
usr

2
" Eğer bir karma tabanlı koleksiyonunda saklamak istiyorsanız hashCode sadece yararlıdır. (C ++ tercihi std :: vector ve düz sırasız listeler için ise) gerçek karşı getirdiği" _hashCodedeğil 'farklı kapsayıcı kullanın' ziyade işaret C ++ 'ların std::unordered_map, eleman sınıfının kendisinin uygulamayı sağlamasını zorunlu kılmak yerine, bir şablon argümanı kullanarak karmaşa yaptığını öğrenin. Yani, C ++ 'daki diğer tüm iyi konteynerler ve kaynak yöneticileri gibi, müdahaleci değil; Birisinin daha sonra bir başkasına ihtiyaç duyması durumunda işlev veya veri içeren tüm nesneleri kirletmez .
underscore_d

100

Çünkü tüm nesneler tarafından paylaşılan fonksiyon yok. Bu arayüze, bütün sınıflar için anlamlı olacak hiçbir şey yok.


10
Cevabın basitliği için +1, bu gerçekten tek sebep.
BWG

7
Tecrübe ettiğim geniş çerçevede ortak ana sınıf, <whatever> bağlamında istenen seri hale getirme ve yansıtma altyapısını sağlar. Meh. Bu sadece insanların veri ve meta verilerle birlikte bir grup zorlanmayı seri hale getirmesiyle sonuçlandı ve veri formatını verimli olamayacak kadar büyük ve karmaşık hale getirdi.
dmckee

19
@dmckee: Ayrıca, serileştirme ve yansıtmanın evrensel olarak pek de faydalı ihtiyaçlar olmadığını da savunuyorum.
DeadMG

16
@DeadMG: "AMA HER ŞEYİ TASARRUF ETMEN GEREKENLER NEDİR?"
deworde

8
Bilmiyorum, tırnak içine sokuyorsun, tüm kepleri kullanıyorsun ve insanlar şakayı göremiyorlar. @ MSalters: Eh, bu kolay olmalı, minimum miktarda devlet var, sadece orada olduğunu belirtin. Özyinelemeli bir döngü girmeden adımı bir listeye yazabilirim.
deworde

25

Ne zaman yüksek kalıtım hiyerarşisi oluşturduğunuzda, Fragile Base Class (Wikipedia.) Problemine rastlarsınız .

Birçok küçük, ayrı (ayrı, yalıtılmış) kalıtım hiyerarşisine sahip olmak, bu soruna girme şansını azaltır.

Tüm nesnelerinizi tek bir münferit kalıtım hiyerarşisinin parçası haline getirmek, bu soruna rastlayacağınızı pratik olarak garanti eder.


6
Temel sınıf (Java'da "java.lang.Object"), diğer yöntemleri çağıran herhangi bir yöntem bulunmadığında, Kırılgan Temel Sınıf sorunu oluşamaz.
Martin Rosenau

3
Olabilecek güçlü, faydalı bir temel sınıf!
Mike Nakis

9
@MartinRosenau ... bir ana üs sınıfına ihtiyaç duymadan C ++ 'ta yapabileceğiniz gibi!
gbjbaanb

5
@ DavorŽdralo Yani C ++ temel bir işlev için aptal bir isme sahip ("DebugPrint" gibi mantıklı bir şey yerine "operatör <<"), Java ise kesinlikle yazdığınız her sınıf için bir istisna yok. Sanırım C ++ 'ın siğilini daha çok seviyorum.
Sebastian Redl,

4
@ DavorŽdralo: İşlevin adı anlamsız. Bir sözdizimi resim cout.print(x).print(0.5).print("Bye\n")- üzerine menteşe yok operator<<.
MSalters

24

Çünkü:

  1. Kullanmadığın kadarını ödememelisin.
  2. Bu işlevler, değer tabanlı bir tür sistemde referans tabanlı bir tür sistemden daha az anlamlıdır.

Uygulama herhangi sıralama virtualişlevi gerekli ne de birçok (çoğu?) Durumlarda istenen ne olduğunu nesne başına uzay yükü gerektiren bir sanal-tablo, tanıtır.

Uygulama toStringbunun geri dönebilirler tek şey Java aksine derece kullanıcı düşmanca olduğunu ve arayan zaten erişime sahip olduğu nesnenin adresi, çünkü nonvirtually oldukça yararsız olacaktır.
Benzer şekilde, sanal olmayan equalsveya hashCodeyalnızca oldukça kullanışsız ve sık sık yanlış olan nesneleri karşılaştırmak için adresleri kullanabiliyordu - Java'dan farklı olarak, nesneler C ++ 'da sık sık kopyalanıyor ve bu nedenle bir nesnenin "kimliğini" ayırt etmek bile değil her zaman anlamlı ya da yararlı. (örneğin bir intgerçekten gerektiği değil eşit olmalıdır ... değeri daha aynı değere sahip iki tamsayılar diğer bir kimliğe sahip.)


Bu konu ve Mike Nakis tarafından belirtilen kırılgan temel sınıf sorunu ile ilgili olarak, temelde tüm yöntemleri dahili olarak (yani aynı sınıftan çağrıldığında) sanal olmayan ancak sanal davranışlarını koruyarak Java ile sabitlemenin ilginç araştırmalarına / önerilerine dikkat edin . harici olarak adlandırılan; eski / standart davranışlar (yani her yerde sanal) için teklif yeni bir anahtar kelime ortaya koydu . Ancak birkaç gazetenin ötesine geçtiğini sanmıyorum. open
Fizz

Bu makale hakkında biraz daha tartışma, lambda-the-ultimate.org/classic/message12271.html
Fizz

Aynı temel sınıfa sahip mümkün test etmek kılacak herhangi shared_ptr<Foo> bir zamanda olup olmadığını görmek için shared_ptr<Bar>(diğer işaretçi türleri ile veya aynı şekilde) bile Foove Barbirbirleri hakkında hiçbir şey bilmiyor ilgisiz sınıflardır. Böyle bir şeyin "ham işaretçilerle" çalışmasını gerektiren, böyle şeylerin nasıl kullanıldığıyla ilgili tarihçesi göz önüne alındığında, pahalı olacaktı, ancak yine de yığın halinde depolanacak olan şeyler için, ilave maliyet minimum olacaktır.
supercat

Her şey için ortak bir temel sınıfa sahip olmak faydalı olmasa da, ortak temel sınıfların yardımcı olacağı oldukça büyük bazı nesne kategorileri olduğunu düşünüyorum. Örneğin, Java’daki birçok (çoğunluk olmasa bile önemli bir çoğulculuk) sınıfı iki şekilde kullanılabilir: paylaşılmamış bir değişken veri taşıyıcısı olarak veya kimsenin değiştirmesine izin verilmeyen paylaşılabilir bir veri taşıyıcısı olarak. Her iki kullanım modelinde de, yönetilen bir işaretçi (başvuru), temel verinin proxy'si olarak kullanılır. Tüm bu veriler için ortak bir yönetilen işaretçi tipine sahip olmak faydalıdır.
supercat

16

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::anybunu yapabilir olmadan ortak bir kök sınıfına. Ve boost::anyayrı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 intveya bir doubleveya 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_togeç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_serializeve std::vectorelde 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.


8

Yukarıda çok iyi cevaplar var ve temel bir nesne sınıfı ile yapacağınız herhangi bir şeyin @ ratchetfreak'in cevabında gösterildiği gibi başka şekillerde daha iyi yapılabileceği ve bunun hakkındaki yorumların çok önemli olduğu açık, ama miras elmasları yaratmamak için başka bir neden daha varçoklu miras kullanıldığında. Evrensel bir temel sınıfta herhangi bir işlevselliğe sahipseniz, birden fazla kalıtım kullanmaya başladığınız anda hangi varyanta erişmek istediğinizi belirtmeye başlamanız gerekir, çünkü kalıtım zincirinin farklı yollarında farklı şekilde aşırı yüklenebilir. Üstelik temel sanal olamaz, çünkü bu çok verimsiz olacaktır (tüm nesnelerin bellek kullanımı ve konum bakımından potansiyel olarak muazzam bir maliyetle sanal bir tabloya sahip olmasını gerektirir). Bu çok hızlı bir şekilde lojistik bir kabus olur.


1
Elmas problemine bir çözüm, neredeyse hiç bir taban tipini çoklu yollardan türetmeyen tüm tiplerin, bu baz tipindeki tüm sanal üyeleri geçersiz kılmasıdır; Eğer dilin içine baştan itibaren ortak bir temel türü yerleştirilmiş olsaydı, bir derleyici varsayılan uygulamaları (zorunlu olarak etkileyici olmasa da) otomatik olarak meşrulaştırabilirdi.
supercat

5

Aslında Microsofts'un erken dönemlerinde C ++ derleyicileri ve kütüphaneleri (Visual C ++ 'ı biliyorum, 16 bit) adında böyle bir sınıfa sahipti CObject.

Bununla birlikte, o zamanlar "şablonların" bu basit C ++ derleyicisi tarafından desteklenmediğini bilmelisiniz, bu yüzden gibi sınıflar std::vector<class T>mümkün değildi. Bunun yerine, bir "vektör" uygulaması yalnızca bir tür sınıfı işleyebildi, bu nedenle std::vector<CObject>bugünle karşılaştırılabilir bir sınıf vardı . Çünkü CObjectneredeyse tüm sınıfların temel sınıfıydı (ne yazık ki değil CString- stringmodern derleyicilerin eşdeğeri ) bu sınıfı neredeyse her türlü nesneyi depolamak için kullanabilirsiniz.

Modern derleyiciler şablonları desteklediğinden, bu "genel bir temel sınıf" kullanım durumu artık verilmez.

Böyle bir genel taban sınıfının kullanılmasının (biraz) hafızaya ve çalışma zamanına mal olacağı gerçeğini düşünmelisiniz - örneğin yapıcı çağrısında. Bu yüzden, böyle bir sınıfı kullanırken sakıncaları var ama en azından modern C ++ derleyicilerini kullanırken, böyle bir sınıf için neredeyse hiç bir kullanım durumu yok.


3
Bu MFC mi? [Yorum doldurma]
user253751

3
Gerçekten de MFC. Dünyaya işlerin nasıl yapılması gerektiğini gösteren parlayan bir OO tasarımı. Oh, bekle ...
gbjbaanb 16:15

4
@gbjbaanb Turbo Pascal ve Turbo C ++, TObjectMFC'nin varlığından önce kendilerine aitti . Tasarımın bu bölümü için Microsoft’u suçlamayın, o zamanlar hemen hemen herkes için iyi bir fikir gibi görünüyordu.
HVD

Şablonlardan önce bile, Smalltalk'ı C ++ ile yazmaya çalışmak korkunç sonuçlar doğurdu.
JDługosz

@hvd Yine de, MFC, Borland’ın ürettiği herhangi bir şeyden çok nesne yönelimli tasarım örneği oldu.
Jules

5

Java'dan gelen başka bir neden önereceğim.

Çünkü sen için bir temel sınıf oluşturamıyor şeyi en azından kazan-plakanın bir demet olmadan.

Kendi sınıflarınız için bununla kaçabilirsiniz - ancak muhtemelen çok sayıda kodun çoğaltıldığını göreceksiniz. Örn: " std::vectorBurada uygulamadığı için kullanamıyorum IObject- IVectorObjectdoğru olanı yapan yeni bir türetme oluştursam daha iyi olur ...".

Bu, yerleşik veya standart kütüphane sınıfları veya diğer kütüphanelerdeki sınıflarla uğraşırken durum böyle olacaktır.

Şimdi, eğer dilin içine yerleştirildiyse, java'daki Integerve intkafa karışıklığı veya dil sözdiziminde büyük bir değişiklik gibi şeylerle karşılaşırsınız . (Diğer dillerin her türün içine yerleştirmede iyi bir iş çıkardığını düşünüyorum - yakut daha iyi bir örnek gibi görünüyor.)

Ayrıca, temel sınıfınız çalışma zamanı polimorfik değilse (yani, sanal işlevleri kullanarak), çerçeve gibi bir özellik kullanmanın aynı avantajı elde edebileceğinizi unutmayın.

Örneğin, bunun yerine .toString(), aşağıdakilere sahip olabilirsiniz: (NOT: Bu temizleyiciyi varolan kütüphaneleri kullanarak yapabileceğinizi biliyorum.

template<typename T>
struct ToStringTrait;

template<typename T> 
std::string toString(const T & t) {
  return ToStringTrait<T>::toString(t);
}

template<>
struct ToStringTrait<int> {
  std::string toString(int v) {
    return itoa(v);
  }
}

template<typename T>
struct ToStringTrait<std::vector<T>> {
  std::string toString(const std::vector<T> &v) {
    std::stringstream ss;
    ss<<"{";
    for(int i=0; i<v.size(); ++i) {
      ss<<toString(v[i]);
    }
    ss<<"}";
    return ss.str();
  }
}

3

Tartışmalı olarak "geçersiz", evrensel bir temel sınıfın rollerinin çoğunu yerine getirir. Herhangi bir işaretçiyi a void*. Daha sonra bu işaretçileri karşılaştırabilirsiniz. Sen edebilirsiniz static_castorijinal sınıfına geri.

Ancak ne edemez ilgisi voidhangi ile yapabilirsiniz ObjectEğer gerçekten sahip nesnenin ne tür anlamaya kullanım RTTI olduğunu. Bu sonuçta, C ++ 'daki tüm nesnelerin RTTI’ya sahip olmamalarına bağlı değildir ve gerçekten sıfır genişlikte nesnelere sahip olmak mümkündür.


1
Yalnızca sıfır genişliğindeki temel sınıf alt nesneleri, normal olanları değil.
Deduplicator

@Deduplicator Güncelleme yoluyla, [[no_unique_address]]üye alt nesnelere sıfır genişlik vermek için derleyiciler tarafından kullanılabilecek C ++ 17 ekler .
underscore_d

1
@underscore_d C ++ 20 için planladınız demek [[no_unique_address]], derleyicinin EBO üye değişkenlerine izin vermesini sağlar.
Deduplicator

@Deduplicator Whoops, evet. C ++ 17'yi kullanmaya başladım, ama sanırım gerçekten olduğundan daha gelişmiş olduğunu düşünüyorum!
underscore_d

2

Java, Tanımlanmamış Davranışın var olmaması gerektiği tasarım felsefesini alır . Gibi kod:

Cat felix = GetCat();
Woofer Rover = (Woofer)felix;
Rover.woof();

Arayüzü uygulayanın felixalt türünü tutup tutmadığını test Catedecek Woofer; eğer öyleyse, oyuncu kadrosunu gerçekleştirecek ve çağıracak woof()ve eğer yapmazsa, bir istisna atacaktır. Kodun davranışı tamamen olmadığı tanımlanır felixuygular Wooferya da değil .

C ++, eğer bir program bir işlem yapmazsa, bu işlem denenirse, oluşturulan kodun ne yapacağının önemi olmaması ve bilgisayarın "yapması gereken" durumlarda davranışı kısıtlamak için zaman kaybetmemesi gerektiği felsefesini alır. asla ortaya çıkma. C ++ 'da uygun dolaylı işleçleri a' ya atmak için *Cateklemek *Woofer, kod meşru olduğunda tanımlanmış davranışı, fakat olmadığı zaman Tanımlanmamış Davranışı verir .

İşler için ortak bir taban tipine sahip olmak, o taban türünün türevleri arasındaki atmaları doğrulamayı ve ayrıca deneme işlemleri yapmayı mümkün kılar, ancak atmaları doğrulamak, meşru olduklarını varsaymaktan ve kötü bir şey olacağını ummamaktan daha pahalı. C ++ felsefesi, böyle bir onaylamanın “[genellikle] ihtiyacınız olmayan bir şey için ödeme yapmayı” gerektireceği şeklinde olacaktır.

C ++ ile ilgili olan, ancak yeni bir dil için sorun olmayacak bir diğer problem, eğer birkaç programcı her biri ortak bir temel oluşturursa, kendi sınıflarını bundan türetirse ve bu ortak temel sınıfın işleriyle çalışmak için kod yazarsa, bu kod, farklı bir temel sınıf kullanan programcılar tarafından geliştirilen nesnelerle çalışamaz. Yeni bir dil, tüm yığın nesnelerinin ortak bir başlık biçimine sahip olmasını gerektiriyorsa ve hiç olmayan yığın nesnelerine izin vermediyse, o zaman böyle bir başlık içeren bir yığın nesnesine başvuru gerektiren bir yöntem, herhangi bir yığın nesnesine bir başvuru kabul eder. hiç yaratabilirdi.

Şahsen, bir nesneyi sormak için "X tipine dönüştürülebilir misiniz?" Diye ortak bir soruna sahip olmanın bir dilde / çerçevede çok önemli bir özellik olduğunu düşünüyorum, ancak böyle bir özellik baştan beri bir dilin içine yerleştirilmemişse daha sonra ekle. Şahsen, böyle bir temel sınıfın ilk fırsatta standart bir kütüphaneye eklenmesi gerektiğini ve polimorfik olarak kullanılacak tüm nesnelerin o tabandan miras almaları gerektiğini kuvvetle tavsiye ederek düşünüyorum. Programcıların her birinin kendi "temel türlerini" uygulamasının sağlanması, farklı insanların kodları arasında nesnelerin geçişini zorlaştırır, ancak birçok programcının devraldığı ortak bir temel türüne sahip olmak bunu kolaylaştırır.

EK

Şablonları kullanarak, "rasgele bir nesne tutucu" tanımlamak ve burada bulunan nesnenin türünü sormak mümkündür; Takviye paketi denilen bir şey içeriyor any. Bu nedenle, C ++ standart bir "herhangi bir şey için tip kontrol edilebilir referans" tipine sahip olmasa da, bir tane oluşturmak mümkündür. Bu, yukarıda bahsedilen problemi, dil standardında bir şey olmaması, yani farklı programcıların uygulamaları arasındaki uyumsuzluk ile çözmez, ancak C ++ 'ın her şeyin türetildiği bir temel tür olmadan nasıl elde ettiğini açıklar: Bir gibi davranan bir şey.


Bu, C ++ , Java ve C # öğelerinde derleme zamanında başarısız olur .
milleniumbug

1
@milleniumbug: Eğer Wooferbir arayüz ise ve Catkalıtsal ise, oyuncu kadrosu meşru olacaktır, çünkü var WoofingCatolan Catve uygulayan (şimdi, muhtemelen gelecekteki) var olabilir Woofer. Java derleme / bağlama model altında, bir yaratılması unutmayın WoofingCatiçin kaynak koduna erişim gerektiren olmaz Catne de Woofer.
supercat

3
C ++ vardır dynamic_cast düzgün bir yayın yapmaya çalıştığınız kolları, Catbir karşı Wooferve soruya cevap verecektir "Eğer konvertibl X yazmanız vardır". C ++ oyuncu kadrosu zorlamana izin verecek, çünkü hey, belki de aslında ne yaptığını biliyorsun, ama gerçekten yapmak istediğin şey bu değilse de sana yardımcı olacak.
Rob K,

2
@RobK: Elbette sözdizimi konusunda haklısınız; Mea Culpa. Dynamic_cast hakkında biraz daha fazla şey okudum ve bir bakıma modern C ++ 'in tüm polimorfik nesneler, nesnenin türünü tanımlamak için gerekli olan alanlarla (tipik olarak bir vtable) bir temel "polimorfik nesne" temel sınıfından türetildiği görülüyor işaretçi, ancak bu bir uygulama detayı). C ++, polimorfik sınıfları bu şekilde tanımlamaz, ancak bir dynamic_castişaretleyiciyi bir polimorfik nesneye işaret ettiği takdirde davranış tanımlamış ve bir anlamsız perspektiften
tanımlamıyorsa

2
... tüm polimorfik nesneler bazı bilgileri aynı düzende saklar ve hepsi polimorfik olmayan nesneler tarafından desteklenmeyen bir davranışı destekler; aklıma göre bu, dil tanımının böyle bir terminoloji kullanıp kullanmadığını, ortak bir temelden türemiş gibi davrandıkları anlamına gelir.
supercat

1

Symbian C ++ aslında belirli bir şekilde davranan tüm nesneler için evrensel bir temel sınıf olan CBase'ye sahipti (özellikle de yığın tahsis ettiyse). Sanal bir yıkıcı sağladı, sınıfın inşaat konusundaki hafızasını sıfırladı ve kopya kurucuyu sakladı.

Bunun arkasındaki mantık gömülü sistemler için bir dildi ve C ++ derleyicileri ve özellikleri 10 yıl önce gerçekten çok kötüydü.

Bütün sınıflar bundan miras kalmadı, sadece birkaçı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.