Neden özel üye işlevlerini başlıklara koyarız?


16

Özel üye değişkenleri neden C ++ başlıklarına koyduğumuzun cevabı, sınıfın büyüklüğünün örneklerin bildirildiği noktalarda bilinmesi gerektiğidir, böylece derleyici yığın hakkında uygun şekilde hareket eden kod üretebilir.

Neden özel üyeleri başlıklara koymalıyız?

Ancak sınıf tanımında özel işlevleri beyan etmenin herhangi bir nedeni var mı?

Alternatif, temelde pimpl deyim olacaktır, ancak gereksiz dolaylama olmaksızın.

Bu dil özelliği tarihsel bir hatadan daha mı fazlası?

Yanıtlar:


11

Özel üye işlevleri olabilir virtualve C ++ (vtable kullanan) ortak uygulamalarında, sınıfın tüm istemcileri tarafından belirli bir sıra ve sanal işlev sayısının bilinmesi gerekir. Bu, sanal üye işlevlerinden biri veya daha fazlası olsa bile geçerlidir private.

Derleyici uygulama seçimlerinin dil spesifikasyonunu etkilememesi gerektiğinden, bu "arabayı atın önüne koymak" gibi görünebilir. Ancak, gerçekte C ++ dilinin kendisi, vtables kullanan çalışan bir uygulama ( Cfront ) ile aynı zamanda geliştirildi .


4
"uygulama seçenekleri dili etkilememelidir" C ++ mantrasının tam tersidir. Spesifikasyon belirli bir uygulamayı zorunlu kılmaz, ancak özellikle etkili bir uygulama seçiminin gereksinimlerini karşılamak için özel olarak hazırlanmış birçok kural vardır.
Ben Voigt

Kulağa çok mantıklı geliyor. Bu konuda bir kaynak var mı?
Praxeolitic

@Praxeolitic: Sanal yöntem tablosu hakkında bilgi edinebilirsiniz .
Greg Hewgill

1
@Praxeolitic - 'Annotated C ++ Refernce Manual'ın ( stroustrup.com/arm.html ) eski bir kopyasını bulun - yazarlar çeşitli uygulama ayrıntıları ve dili nasıl şekillendirdiği hakkında konuşuyor.
Michael Kohne

Bunun soruyu gerçekten cevapladığından emin değilim. Bana göre sadece belirli bir yönü ele alıyor - iyi de olsa - gerisini geride bırakıyor: Neden sanal olmayan özel üye işlevlerini başlıklara koymamız gerekiyor ? Tabii, sadece tutarlılık / basitlik için bir argüman - ama ideal olarak biz de mekanik ve / veya felsefi bir mantığımız olurdu. Bu yüzden, bunu çok yararlı etkileri olan çok kasıtlı bir tasarım seçeneği olarak açıkladığına inandığım bir cevap ekledim.
underscore_d

13

Bir sınıfın tanımının dışındaki yöntemlere eklenmesine izin verdiyseniz, bunlar herhangi bir yere , herhangi bir dosyaya, herkes tarafından eklenebilir .

Bu, hemen tüm istemci koduna özel ve korunan veri üyelerine önemsiz erişim sağlayacaktır.

Sınıf tanımını tamamladıktan sonra, bazı dosyaları yazar tarafından genişletmek için özel olarak kutsanmış olarak işaretlemenin bir yolu yoktur - sadece düz çeviri birimleri vardır. Bu nedenle, derleyiciye belirli bir yöntem kümesinin resmi olduğunu veya sınıf yazarı tarafından kutsandığını söylemenin tek makul yolu, bunları sınıf içinde bildirmektir.


C ++ 'da belleğe doğrudan erişebildiğimizi unutmayın, yani sınıfınızla aynı bellek düzenine sahip bir gölge türü oluşturmak, kendi yöntemlerimi eklemek (veya tüm verileri herkese açık hale getirmek) ve reinterpret_cast . Ya da özel fonksiyonunuzun kodunu bulabilir veya sökebilirim. Veya sembol tablosundaki fonksiyon adresini arayın ve doğrudan veya arayın.

Bu erişim belirteçleri bu saldırıları önlemeye çalışmaz, çünkü bu mümkün değildir. Sadece bir sınıfın nasıl kullanılması gerektiğini gösterirler.


1
Sınıf tanımı, istemci kodundan gizlenmiş bir işlev kabı varlığına atıfta bulunabilir. Alternatif olarak, bu tersine çevrilebilir ve istemci kodundan gizlenen tam bir geleneksel sınıf tanımı, yalnızca herkese açık üyeleri tanımlayan ve boyutunu ortaya çıkarabilen görünür bir arayüz belirleyebilir.
Praxeolitic

1
Tabii, ama derleme modeli gizli kod kendi sürümünü veya ekstra erişimciler ile farklı bir görünür arabirimi araya istemci kodu durdurmak için makul bir yol sağlamaz.
Yararsız

Bu iyi bir nokta, ancak yine de başlıkta yer alan tüm üye işlevlerin tanımları için doğru değil mi?
Praxeolitic

1
Tüm üye işlevleri sınıf içinde bildirilir . Mesele şu ki, en azından sınıf tanımında bildirilmeyen yeni üye işlevleri ekleyemezsiniz (ve bir tanım kuralı birden fazla tanımı önler).
Yararsız

Peki ya özel fonksiyonlar kuralının bir kabı?
Praxeolitic

8

Kabul edilen cevap, bunu sanal özel işlevler için açıklar , ancak bu OP'nin sorduğundan çok daha sınırlı olan sorunun sadece belirli bir yönünü yanıtlar. Bu yüzden, yeniden ifade etmemiz gerekiyor: Neden başlıklarda sanal olmayan özel işlevleri bildirmemiz gerekiyor ?

Başka bir cevap, sınıfların bir blokta bildirilmesi gerektiğini - bundan sonra mühürlendiklerini ve eklenemeyeceklerini ortaya koymaktadır. Başlıkta özel bir yöntem bildirmeyi ve ardından başka bir yerde tanımlamaya çalışarak bunu yapardınız. Güzel nokta. Sınıfın bazı kullanıcıları neden diğer kullanıcıların gözlemleyemeyeceği şekilde genişletebilir? Özel yöntemler bunun bir parçasıdır ve bundan hariç tutulmaz. Ama sonra neden dahil edildiğini soruyorsunuz ve bu biraz totolojik görünüyor. Sınıf kullanıcıları neden bunları bilmeli? Görünmüyorlarsa kullanıcılar ekleyemediler ve hey presto.

Bu nedenle, varsayılan olarak özel yöntemleri dahil etmek yerine, kullanıcılara görünür olmalarını sağlamak için belirli noktalar sağlayan bir cevap vermek istedim. Kamu beyanı gerektiren sanal olmayan özel fonksiyonların mekanik bir nedeni, Herb Sutter'in Mantık # 100 hakkında gerekçesinin bir parçası olarak GotW # 100'de verilmiştir . Burada Pimpl hakkında konuşmayacağım, çünkü eminim hepimiz biliyoruz. Ama işte ilgili bit:

Üstbilgi dosyası sınıfı tanımındaki herhangi bir şey değiştiğinde C ++ 'da, sınıftaki kullanıcıların erişemediği özel sınıf üyelerinde tek değişiklik olsa bile, bu sınıfın tüm kullanıcıları yeniden derlenmelidir. Bunun nedeni, C ++ 'ın derleme modelinin metin içerme üzerine kurulu olması ve C ++' ın arayanların özel üyelerden etkilenebilecek bir sınıf hakkında iki ana şey bildiğini varsaymasıdır:

  • Boyut ve Düzen : [üyeler ve sanal işlevler - açıklayıcı ve performans için harika, ama neden burada olduğumuzu değil]
  • İşlevler : Arama kodu, özel olmayan işlevlerle aşırı yüklenen erişilemeyen özel işlevler de dahil olmak üzere sınıfın üye işlevlerine yapılan çağrıları çözebilmelidir - özel işlev daha iyi eşleşiyorsa, arama kodu derlenemez. (C ++, güvenlik nedenleriyle erişilebilirlik kontrolünden önce aşırı yük çözünürlüğü gerçekleştirmek için kasıtlı tasarım kararı aldı. Örneğin, bir işlevin erişilebilirliğini özelden kamuya değiştirmenin yasal arama kodunun anlamını değiştirmemesi gerektiği hissedildi.)

Sutter, elbette, Komite üyesi olarak son derece güvenilir bir kaynaktır, bu yüzden birini gördüğünde "kasıtlı bir tasarım kararı" bilir. Değişmiş anlambilimden ya da yanlışlıkla kırılabilir erişilebilirlikten kaçınmanın bir yolu olarak özel yöntemlerin kamuya açıklanmasını zorunlu kılma fikri muhtemelen en ikna edici mantıktır. Neyse ki, her şey şimdiye kadar oldukça anlamsız göründüğü için!


Ama sonra neden soruyorsun ve bu cevap totolojik görünüyor. Yine, sınıf kullanıcılarının neden onlar hakkında bilgi sahibi olması gerekiyor? ” Hayır, bu totolojik değil. Kullanıcıların mutlaka "onlar hakkında bilgi sahibi olmaları" gerekmez. Gerçekleşmesi gereken şey, sınıf yazarının rızası olmadan insanların sınıfları genişletmesini engelleyebilmenizdir. Bu nedenle, bu tür üyelerin tümü açık ilan edilmelidir. Bunun, kullanıcıların özel üyeler hakkında bilgi sahibi olmalarının yalnızca gerekli bir sonuçtur.
Nicol Bolas

1
@NicolBolas Kesinlikle. Bu muhtemelen benim açımdan çok iyi bir ifade değildi. Cevabın, özel yöntemlerin görünürlüğünü, yalnızca özel yöntemlerin görünürlüğü hakkında bir gerekçe sağlamak yerine, birçok şeyi kapsayan (çok geçerli) bir kuralın sonucu olarak açıkladığını kastetmiştim. Tabii ki, gerçek cevap Useless'in, gnasher'ın ve benimkinin bir kombinasyonudur. Sadece burada olmayan başka bir bakış açısı sunmak istiyorum.
underscore_d

4

Bunu yapmanın iki nedeni var.

İlk olarak, erişim belirticisinin derleyici için olduğunu ve çalışma zamanında ilgili olmadığını unutmayın. Kapsam dışında özel bir üyeye erişim derleme hatasıdır.

Özlülük

Kısa, bir veya iki satırlı bir işlev düşünün. Başka bir yerde kodun çoğaltılmasını azaltmak için var, bu da bir algoritmanın veya başka bir şeyin nasıl çalıştığını birçok yerine tek bir yerde (örneğin bir sıralama algoritmasını değiştirme) değiştirebilme avantajına sahip.

Üstbilgide hızlı bir veya iki satır mı yoksa işlev prototipi artı bir yerde bir uygulama mı olmasını tercih edersiniz? Üstbilgide bulmak daha kolaydır ve kısa işlevler için ayrı bir uygulamaya sahip olmak çok daha ayrıntılıdır.

Bir başka büyük avantajı daha var ...

Satır İçi İşlevler

Özel bir işlev satır içine alınabilir ve bu mutlaka üstbilgide olmasını gerektirir. Bunu düşün:

class A {
  private:
    inline void myPrivateFunction() {
      ...
    }

  public:
    inline void somePublicFunction() {
      myPrivateFunction();
      ...
    }
};

Özel fonksiyon olabilir kamu görevi ile birlikte satır içine yerleştirilmiş muktedir. Bu, derleyicinin takdirine bağlı olarak yapılır, çünkü inlineanahtar kelime teknik olarak bir öneri değildir, bir gereklilik değildir.


Sınıf gövdesinde tanımlanan tüm işlevler otomatik olarak işaretlenir inline, anahtar kelimeyi burada kullanmak için bir neden yoktur.
Ben Voigt

@BenVoigt öyle. Bunu fark etmedim ve bir süredir C ++ yarı zamanlı kullanıyorum. O dil beni böyle küçük nugget'larla şaşırtmaktan asla vazgeçmiyor.

Bir yöntemin satır içinde olması için başlıkta olması gerektiğini düşünmüyorum. Sadece aynı derleme biriminde olması gerekir. Bir sınıf için ayrı derleme birimlerine sahip olmanın mantıklı olduğu bir durum var mı?
Samuel Danielson

@SamuelDanielson doğru, ancak sınıf tanımında özel bir işlev olmalıdır. "Özel" kavramı sınıfın bir parçası olduğunu ima eder. .cppSınıf tanımının dışında tanımlanan üye işlevleri tarafından satır içine alınan dosyada üye olmayan bir işlev olması mümkün olabilir , ancak böyle bir işlev özel olmaz.

Sorunun gövdesi, " sınıf tanımında [sic, bağlamın OP'nin gerçekten sınıf beyanı anlamına geldiğini gösterdiği sicil) özel işlevleri beyan etmenin herhangi bir nedeni var mı? " İdi. Özel fonksiyonları tanımlamaktan bahsediyorsun. Bu soruya cevap vermiyor. @SamuelDanielson Günümüzde LTO, fonksiyonların bir projenin herhangi bir yerinde olabileceği ve satır içi olma şansının eşit olacağı anlamına geliyor. Bir sınıfı birden çok çeviri birimine bölmeye gelince, en basit durum sınıfın sadece büyük olması ve onu semantik olarak birden çok kaynak dosyaya bölmek istemenizdir. Eminim böyle büyük sınıflar cesaretini kırmıştır, ama yine de
underscore_d

2

Başlık dosyasında özel yöntemlere sahip olmanın başka bir nedeni: Genel bir satır içi yöntemin bir veya birkaç özel yöntemi çağırmaktan çok daha fazla olmadığı durumlar vardır. Üstbilgide özel yöntemlere sahip olmak, genel yönteme yapılan bir çağrının, özel yöntemlerin gerçek koduna tamamen satır içine alınabileceği anlamına gelir ve satır içi özel yöntem çağrısıyla durmaz. Farklı bir derleme biriminden bile (ve genel yöntemler genellikle farklı derleme birimlerinden çağrılabilir).

Tabii ki, özel olanlar da dahil olmak üzere tüm yöntemleri bilmiyorsa, derleyicinin aşırı yük çözünürlüğü ile ilgili sorunları tespit edememesinin nedeni de vardır.


Inlining hakkında mükemmel bir nokta! Bunun özellikle stdlib ve diğer kütüphaneler için satır içi olarak tanımlanan yöntemlerle ilgili olduğunu düşünüyorum. (
underscore_d

0

Bu işlevlerin özel üyelere erişmesine izin vermek içindir. Aksi takdirde friendyine de başlıkta onlara ihtiyacınız olacaktır.

Herhangi bir işlev sınıfın özel üyelerine erişebilseydi, o zaman özel işe yaramazdı.


1
Belki soruda daha açık olmalıydım - demek istediğim dil neden bu şekilde tasarlandı? Neden olmalı ya da bu tasarım neden iyi bir tasarım?
Praxeolitic
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.