dostęp do chronionego członka klasy bazowej w innej podklasie

Dlaczego to kompiluje:

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

class Foo : public FooBase
{
public:
    void foo(Foo& fooBar)
    {
        fooBar.fooBase();
    }
};
Ale tak nie jest?
class FooBase
{
protected:
    void fooBase(void);
};

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

Z jednej strony C++ przyznaje dostęp do prywatnych/chronionych członków dla wszystkich instancji tej klasy, ale z drugiej strony nie przyznaje dostępu do chronionych członków klasy bazowej dla wszystkich instancji podklasy. To wygląda raczej niekonsekwentnie.

Testowałem kompilację z VC++ i z ideone.com i oba kompilują pierwszy, ale nie drugi fragment kodu.

Author: iammilind, 2012-07-24

7 answers

Kiedy foo otrzymuje referencję FooBase, kompilator nie wie, czy argument jest potomkiem Foo, więc musi założyć, że nie jest. Foo ma dostęp do dziedziczonych chronionych członków innych Foo obiektów, nie wszystkich innych klas rodzeństwa.

Rozważ ten kod:

class FooSibling: public FooBase { };

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

Jeśli Foo::foo może wywoływać protected członków arbitralnych Potomków FooBase, to może wywoływać protected metodę FooSibling, która nie ma bezpośredniego związku z Foo. To nie tak. chroniony dostęp powinien działać.

Jeśli Foo potrzebuje dostępu do chronionych członków wszystkich FooBase obiektów, nie tylko tych, które są również znane jako Foo potomkowie, to Foo musi być przyjacielem FooBase:

class FooBase
{
protected:
  void fooBase(void);
  friend class Foo;
};
 30
Author: Rob Kennedy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 13:42:33

C++ FAQ ładnie podsumowuje ten problem:

[wolno Ci wybierać własne kieszenie, ale nie wolno Ci wybierać kieszeni ojca ani brata.

 19
Author: h0b0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-25 12:30:09

Najważniejsze jest to, że protected daje Ci dostęp do własnej kopii członka, a nie do członków w jakimkolwiek innym obiekcie. Jest to powszechne błędne przekonanie, ponieważ częściej niż nie uogólniamy i stwierdzamy protected przyznaje dostęp do elementu typowi pochodnemu (bez wyraźnego stwierdzenia, że tylko do ich własnych baz...)

Teraz, to jest z jakiegoś powodu, i ogólnie nie powinieneś uzyskać dostępu do członka w innej gałęzi hierarchii, ponieważ możesz złamać niezmienniki na które inne obiekty zależą. Rozważmy typ, który wykonuje kosztowne obliczenia na niektórych dużych elementach danych (chronionych) i dwa typy pochodne, które buforują wynik według różnych strategii: {]}

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(); 
   }
};

Typ cache_on_read przechwytuje modyfikacje danych i zaznacza wynik jako nieprawidłowy, tak że następny odczyt wartości zostanie ponownie obliczony. Jest to dobre podejście, jeśli liczba zapisów jest stosunkowo wysoka, ponieważ wykonujemy obliczenia tylko na żądanie (tj. wielokrotne modyfikacje nie spowoduje przeliczania). cache_on_write wstępnie oblicza wynik z góry, co może być dobrą strategią, jeśli liczba zapisów jest mała, a chcesz deterministyczne koszty odczytu (pomyśl o małym opóźnieniu odczytu).

Wróćmy do pierwotnego problemu. Obie strategie cache utrzymują bardziej rygorystyczny zbiór niezmienników niż baza. W pierwszym przypadku dodatkowym niezmiennikiem jest to, że cached jest true tylko wtedy, gdy data nie został zmodyfikowany po ostatnim odczycie. W drugim przypadku dodatkowe niezmienne jest to, że result_value jest wartością operacji przez cały czas.

Jeśli trzeci typ Pochodny odwołał się do base i wszedł do data, aby napisać (jeśli protected na to pozwolił), to zerwałby z niezmiennikami typów pochodnych.

To powiedziawszy, specyfikacja języka jest złamana (osobista opinia), ponieważ pozostawia tylne drzwi do osiągnięcia tego konkretnego rezultatu. W szczególności, jeśli utworzysz wskaźnik do członka członka z bazy w Typ Pochodny, dostęp jest sprawdzany w derived, ale zwracany wskaźnik jest wskaźnikiem do elementu base, który może być zastosowany do dowolnego base obiekt:

class base {
protected:
   int x;
};
struct derived : base {
   static void modify( base& b ) {
      // b.x = 5;                        // error!
      b.*(&derived::x) = 5;              // allowed ?!?!?!
   }
}
 10
Author: David Rodríguez - dribeas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 15:21:29

W obu przykładach Foo dziedziczy chronioną metodę fooBase. Jednakże, w pierwszym przykładzie próbujesz uzyskać dostęp do podanej metody chronionej z tej samej klasy (Foo::foo wywołuje Foo::fooBase), podczas gdy w drugim przykładzie próbujesz uzyskać dostęp do chronionej metody z innej klasy, która nie jest zadeklarowana jako friend class (Foo::foo próbuje wywołać FooBase::fooBase, co się nie powiedzie, później jest chronione).

 3
Author: Zeta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 13:25:06

W pierwszym przykładzie przekazujesz obiekt typu Foo, który oczywiście dziedziczy metodę fooBase () i jest w stanie ją wywołać. W drugim przykładzie próbujesz wywołać funkcję chronioną, po prostu tak, niezależnie od tego, w jakim kontekście nie możesz wywołać funkcji chronionej z instancji klasy, w której jest zadeklarowana. W pierwszym przykładzie dziedziczysz chronioną metodę fooBase, więc masz prawo wywołać ją w kontekście Foo

 1
Author: Moataz Elmasry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 13:29:05

Widzę rzeczy w kategoriach pojęć i wiadomości. Jeśli twoja metoda FooBase została faktycznie nazwana "SendMessage", a Foo "English Speakingperson", a FooBase SpeakingPerson, Twoja protected deklaracja ma na celu ograniczenie SendMessage do osób mówiących po angielsku (i podklas np.: AmericanEnglishSpeakingPerson, AustralianEnglishSpeakingPerson) . Inny typ osoby mówiącej wywodzącej się z osoby mówiącej nie byłby w stanie otrzymać wiadomości SendMessage, chyba że zadeklarował francuskojęzycznego jako przyjaciela, gdzie "przyjaciel" oznacza, że francuskojęzyczny ma specjalną zdolność do odbierania SendMessage od anglojęzycznego (czyli może zrozumieć angielski).

 1
Author: Sentinel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 14:00:36

Oprócz odpowiedź hobo Możesz szukać obejścia.

Jeśli chcesz, aby podklasy chciały wywołać metodę fooBase, możesz to zrobić static. statyczne metody chronione są dostępne przez podklasy z wszystkimi argumentami.

 0
Author: yairchu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:26