web-gelistirme-sc.com

bir temel sınıfın korumalı bir üyesine başka bir alt sınıfta erişilmesi

Bu neden derler:

class FooBase
{
protected:
    void fooBase(void);
};

class Foo : public FooBase
{
public:
    void foo(Foo& fooBar)
    {
        fooBar.fooBase();
    }
};

ama bu değil mi?

class FooBase
{
protected:
    void fooBase(void);
};

class Foo : public FooBase
{
public:
    void foo(FooBase& fooBar)
    {
        fooBar.fooBase();
    }
};

Bir yandan C++, o sınıfın tüm örnekleri için özel/korumalı üyelere erişim izni verir, ancak diğer yandan, bir alt sınıfın tüm örnekleri için bir temel sınıfın korumalı üyelerine erişim izni vermez. bana göre.

VC++ ve ideone.com ile derlemeyi test ettim ve her ikisi de ilk kod parçacığını değil, birinci kod parçasını derledi.

34
Kaiserludi

foo bir FooBase referansı aldığında, derleyici, argümanın Foo'nin soyundan olup olmadığını bilmez, bu yüzden olmadığını varsayması gerekir. Foo, diğer Foo nesnelerinin / - diğer kardeş sınıflarına değil, korunan üyelerine erişebilir.

Bu kodu göz önünde bulundurun:

class FooSibling: public FooBase { };

FooSibling sib;
Foo f;
f.foo(sib); // calls sib.fooBase()!?

Foo::foo isteğe bağlı FooBase soyundan korunan üyelerini çağırabilirse, FooSibling ile doğrudan ilişkisi olmayan, Foo öğesinin korumalı yöntemini çağırabilir. Korumalı erişimin çalışması gerektiği gibi değil.

Foo __ FooBase nesnelerinin korumalı üyelerine erişime ihtiyaç duyuyorsa, yalnızca Foo torunları olarak da bilinenler değil, Foo __ FooBase arkadaşı olmalı:

class FooBase
{
protected:
  void fooBase(void);
  friend class Foo;
};
30
Rob Kennedy

C++ FAQ bu sorunu güzel bir şekilde özetliyor:

[Siz] kendi ceplerinizi seçmenize izin verilir, ancak babanızın ceplerini veya kardeşinizin ceplerini seçmenize izin verilmez.

20
h0b0

Temel nokta, protected öğesinin any başka bir nesnedeki üyelere değil, üyeye ait kendi kopyanıza erişmesine izin vermesidir. Bu yaygın bir yanılgıdır, çünkü çoğu zaman genelleştirmeziz ve protected üyesine türetilmiş türe erişim izni veririz (açıkça yalnızca kendi tabanlarına açıkça belirtmeden ...)

Şimdi, bu bir nedenden ötürü ve genel olarak üyeye, diğer nesnelerin bağlı olduğu değişmezleri kıracağınız için hiyerarşinin farklı bir kolundan erişmemelisiniz. Bazı büyük veri üyelerinde (korumalı) pahalı bir hesaplama yapan ve farklı stratejileri izleyerek sonucu önbellekte tutan iki türevi göz önünde bulundurun:

class base {
protected:
   LargeData data;
// ...
public:
   virtual int result() const;      // expensive calculation
   virtual void modify();           // modifies data
};
class cache_on_read : base {
private:
   mutable bool cached;
   mutable int cache_value;
// ...
   virtual int result() const {
       if (cached) return cache_value;
       cache_value = base::result();
       cached = true;
   }
   virtual void modify() {
       cached = false;
       base::modify();
   }
};
class cache_on_write : base {
   int result_value;
   virtual int result() const {
      return result_value;
   }
   virtual void modify() {
      base::modify();
      result_value = base::result(); 
   }
};

cache_on_read tipi, verilerde yapılan değişiklikleri yakalar ve sonucu geçersiz olarak işaretler; böylece değerin bir sonraki okuması yeniden hesaplanır. Bu, eğer istek üzerine hesaplamayı yaptığımız için yazma sayısı nispeten yüksekse, bu iyi bir yaklaşımdır (yani, çoklu değişiklikler yeniden hesaplamalara neden olmaz). cache_on_write, sonucu önceden belirler; bu, yazma sayısının az olması durumunda iyi bir strateji olabilir ve okuma için belirleyici maliyetler istersiniz (okumalardaki düşük gecikmeyi düşünün).

Şimdi, asıl soruna dönelim. Her iki önbellek stratejisi, tabandan daha katı bir değişmez kümesini korur. İlk durumda, fazladan değişmeyen, cached __ true, ancak data son okumadan sonra değiştirilmediyse olmasıdır. İkinci durumda, ekstra değişmez, result_value işleminin her zaman değeridir.

Üçüncü türetilmiş bir tür base başvurusuna başvurduysa ve data dosyasına yazılırsa (protected izin verirse), türetilmiş türlerin değişmezleriyle kırılırdı.

Söylendiği üzere, dilin özellikleri kırılmış (kişisel görüş), çünkü bu özel sonucu elde etmek için bir arka kapı bırakıyor. Özellikle, türetilmiş bir türdeki bir tabandan bir üyenin üyesine bir işaretçi oluşturursanız, erişim derived dosyasında işaretlenir, ancak döndürülen işaretçi, anybase öğesine uygulanabilen base üyesine işaretçidir. nesne:

class base {
protected:
   int x;
};
struct derived : base {
   static void modify( base& b ) {
      // b.x = 5;                        // error!
      b.*(&derived::x) = 5;              // allowed ?!?!?!
   }
}

Her iki örnekte de Foo, fooBase korumalı bir yöntemi devralır. Ancak, ilk örneğinizde, aynı sınıftan verilen korumalı yönteme erişmeye çalışıyorsunuz (Foo::foo calls Foo::fooBase), ikinci örnekte ise arkadaş sınıfı olarak bildirilmeyen başka bir sınıftan korumalı bir yönteme erişmeye çalışıyorsunuz (Foo::foo çalışır FooBase::fooBase işlevini çağırmak için başarısız olan daha sonra korunur).

3
Zeta

İlk örnekte, açıkça fooBase () yöntemini miras alan Foo türünde bir nesne iletirsiniz ve bu nedenle onu çağırabilir. İkinci örnekte, korumalı bir işlev çağırmaya çalışıyorsunuz, basitçe, hangi bağlamda, korumalı bir işlevi çağırdığı sınıf örneğinden hangi bağlamda olursa olsun . ve böylece Foo bağlamında onu aramak için hakkınız var.

1
Moataz Elmasry

Şeyleri kavramlar ve mesajlar olarak görme eğilimindeyim. FooBase yönteminiz aslında "SendMessage" olarak adlandırılmışsa ve Foo "EnglishSpeakingPerson" ise ve FooBase SpeakingPerson ise, korumalı bildiriminiz, SendMessage'ı EnglishSpeakingPersons (ve alt sınıfları, örneğin: AmericanEnglishSpeakingPerson, AustralianEnglishPpearingpearpates) ile kısıtlamak için tasarlanmıştır. SpeakingPerson'dan türetilen bir başka FrenchSpeakingPerson türü, bir arkadaş olarak FrenchSpeakingPerson'ı bir arkadaş olarak ilan etmediğiniz sürece bir SendMessage alamazdı; burada 'friend', FrenchSpeakingPerson'un, EnglishSpeakingPerson'dan SendMessage alma özel bir yeteneğine sahip olduğu anlamına geliyordu (yani İngilizce'yi öğrenebilir).

1
Sentinel

hobo'nun cevabına ek olarak bir geçici çözüm isteyebilirsiniz.

Alt sınıfların fooBase yöntemini çağırmasını istiyorsanız, static öğesini yapabilirsiniz. statik korumalı yöntemlere tüm argümanlarla alt sınıflar tarafından erişilebilir.

0
yairchu