Perché la regola dominante del C ++ non interessa le modifiche alla visibilità?

3

In C ++, è possibile scrivere un override per il metodo di una classe base anche se la dichiarazione di visibilità dei due non corrisponde. Quali sono le possibili considerazioni sulla progettazione in base alla decisione di non considerare la visibilità nella regola di sovrascrittura?

Considera questo pezzo di codice come un esempio:

class A{
    public: virtual void f() { cout << "A::f" << endl; }
};

class B : public A {
    private: void f() { cout << "B::f" << endl; }
};

int main() {
    A* a = new B;
    a->f();
}

Quanto sopra compila in clang, e l'esecuzione stampa B :: f, mostrando che è possibile chiamare una funzione privata di B al di fuori della classe, interrompendo così l'incapsulamento.

Non vedo davvero perché questo tipo di comportamento dovrebbe essere consentito. Non è chiaramente per ragioni di prestazioni / efficienza, dal momento che verificare staticamente che due dichiarazioni di visibilità coincidono è banale. Qualcuno ha un'idea o ipotesi su quale possa essere la decisione di progettazione dietro a questo?

    
posta fsestini 23.01.2016 - 20:16
fonte

3 risposte

7

The above compiles in clang, and running it prints B::f, showing that it is possible to call a private function of B from outside the class, thus breaking encapsulation.

L'incapsulamento non è rotto. È B semi-rotto. B eredita pubblicamente l'interfaccia pubblica di A. A nell'interfaccia pubblica di è B. La vera domanda è perché B dovrebbe essere autorizzato a creare persino un override privato di una funzione pubblica.

Il semplice fatto è che non c'è un vero motivo per vietarlo. Se stai scrivendo B e lo fai correttamente, questo non dovrebbe mai accadere nella realtà. A meno che tu non l'abbia esplicitamente inteso per qualche motivo, nel qual caso, congratulazioni.

    
risposta data 23.01.2016 - 20:47
fonte
2

Per quello che vale, in realtà ha un uso semi-pratico. Nello scenario che hai impostato, puoi utilizzare solo oggetti di tipo A o B tramite l'interfaccia definita da A . La funzione B::f è richiamabile solo tramite virtual dispatch, non direttamente.

Ad esempio:

B * pb = new B {};
A * pa = pb;
pa->f();       // fine, calls B::f via virtual dispatch
pa->A::f();    // fine, calls A::f directly
pa->B::f();    // error, B::f is private
pb->f();       // error, B::f is private
pb->A::f();    // fine, calls A::f directly
pb->B::f();    // error, B::f is private

Potresti usare questo per scoraggiare le persone a seconda della tua implementazione e piuttosto programmare contro l'astrazione come dovrebbero. In pratica, tuttavia, preferirei nascondere l'esistenza di B del tutto e fornire solo una funzione di fabbrica che dia ai clienti un% polimorfo di% di tipo dinamico non specificato.

    
risposta data 24.01.2016 - 07:14
fonte
1

È assolutamente intenzionale. La modifica della visibilità deve (al massimo) cambiare se il codice viene compilato o non compilato. Non deve mai, mai cambiare ciò che fa il codice. Se B :: f () fosse pubblico, ci si aspetterebbe che B :: f () venga chiamato. Il fatto che tu abbia reso privato b :: f () non può cambiare questo, secondo la regola sopra; è solo permesso di cambiare se il codice viene compilato. Poiché il chiamante non ha nemmeno bisogno di sapere della classe B, il codice deve compilare e chiamare B :: f ().

Qui non c'è interruzione di incapsulamento. Nella classe A hai dichiarato che f è una funzione virtuale che può essere chiamata da chiunque attraverso un'istanza di A. Le sottoclassi non possono fare nulla per cambiarlo. Se uno sviluppatore pensava che far sì che B :: f () privato impedisse di chiamare la funzione virtuale, allora lo sviluppatore sarebbe solo deluso.

    
risposta data 23.01.2016 - 21:03
fonte

Leggi altre domande sui tag