Privé virtuele methode in C++

Wat is het voordeel van het virtueel maken van een privémethode in C++?

Ik heb dit opgemerkt in een open source C++-project:

class HTMLDocument : public Document, public CachedResourceClient {
private:
    virtual bool childAllowed(Node*);
    virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
};

Antwoord 1, autoriteit 100%

Herb Sutter heeft het hierheel mooi uitgelegd.

Richtlijn #2: Maak virtuele functies liever privé.

Hierdoor kunnen de afgeleide klassen de functie overschrijven om de
gedrag naar behoefte, zonder de virtuele functies verder bloot te leggen
rechtstreeks door ze oproepbaar te maken door afgeleide klassen (zoals zou zijn)
mogelijk als de functies gewoon beveiligd waren). Het punt is dat
virtuele functies bestaan ​​om maatwerk mogelijk te maken; tenzij ze ook nodig hebben
om rechtstreeks vanuit de code van afgeleide klassen te worden aangeroepen, is er geen
moet ze ooit alles behalve privé maken


Antwoord 2, autoriteit 53%

Als de methode virtueel is, kan deze worden overschreven door afgeleide klassen, zelfs als deze privé is. Wanneer de virtuele methode wordt aangeroepen, wordt de overschreven versie aangeroepen.

(In tegenstelling tot Herb Sutter geciteerd door Prasoon Saurav in zijn antwoord, de C++ FAQ Lite beveelt aan tegen privévirtuals, vooral omdat het mensen vaak in verwarring brengt.)


Antwoord 3, autoriteit 9%

Ondanks alle oproepen om een ​​virtueel lid privé te verklaren, gaat het argument gewoon niet op. Vaak zal de onderdrukking van een virtuele functie door een afgeleide klasse de versie van de basisklasse moeten aanroepen. Het kan niet als het privateis verklaard:

class Base
{
 private:
 int m_data;
 virtual void cleanup() { /*do something*/ }
 protected:
 Base(int idata): m_data (idata) {}
 public:
 int data() const { return m_data; }
 void set_data (int ndata) { m_data = ndata; cleanup(); }
};
class Derived: public Base
{
 private:
 void cleanup() override
 {
  // do other stuff
  Base::cleanup(); // nope, can't do it
 }
 public:
 Derived (int idata): base(idata) {}
};

U moetde basisklassemethode protectedverklaren.

Dan moet je het lelijke middel nemen om via een opmerking aan te geven dat de methode moet worden overschreven, maar niet moet worden aangeroepen.

class Base
{
 ...
 protected:
 // chained virtual function!
 // call in your derived version but nowhere else.
 // Use set_data instead
 virtual void cleanup() { /* do something */ }
 ...

Dus de richtlijn van Herb Sutter #3… Maar het paard is toch uit de stal.

Als je iets protectedverklaart, vertrouw je er impliciet op dat de schrijver van een afgeleide klasse de beschermde internals begrijpt en correct gebruikt, net zoals een friend-verklaring een dieper vertrouwen voor privateleden.

Gebruikers die slecht gedrag vertonen door het schenden van dat vertrouwen (bijvoorbeeld als ‘onbegrijpelijk’ bestempeld door niet de moeite te nemen uw documentatie te lezen), hebben alleen zichzelf de schuld.

Update: ik heb feedback gekregen waarin wordt beweerd dat je implementaties van virtuele functies op deze manier kunt “ketenen” met behulp van virtuele privéfuncties. Als dat zo is, zou ik het zeker graag willen zien.

De C++-compilers die ik gebruik, laten zeker niet toe dat een afgeleide klasse-implementatie een private basisklasse-implementatie aanroept.

Als de C++-commissie “privé” zou versoepelen om deze specifieke toegang toe te staan, zou ik helemaal voor privé virtuele functies zijn. Zoals het er nu uitziet, krijgen we nog steeds het advies om de staldeur op slot te doen nadat het paard is gestolen.


4, Autoriteit 3%

Ik gebruik ze om afgeleide klassen toe te staan ​​om “de lege plekken in te vullen” voor een basisklasse zonder een dergelijk gat tot eindgebruikers bloot te leggen. Ik heb bijvoorbeeld zeer bevelzame voorwerpen uit een gemeenschappelijke basis, die slechts 2/3 van de totale staatsmachine kan implementeren (de afgeleide klassen bieden de resterende 1/3 afhankelijk van een sjabloonargument en de basis kan geen sjabloon voor zijn andere redenen).

Ik moet de gemeenschappelijke basisklasse hebben om veel van de publieke API’s correct te maken (ik gebruik Variadische sjablonen), maar ik kan dat object niet in het wild toestaan. Erger, als ik de kraters in de staatsmachine verlaat – in de vorm van zuivere virtuele functies – overal maar in “privé”, laat ik een slimme of clueless gebruiker toe die uit een van zijn kinderlessen afkomstig is om methoden te negeren die gebruikers nooit zouden moeten aanraken. Dus leg ik de staatsmachine ‘Brains’ in privévirtuele functies. Vervolgens vullen de onmiddellijke kinderen van de basisklasse de lege plekken in op hun niet-virtuele overrides, en gebruikers kunnen de resulterende objecten veilig gebruiken of hun eigen verdere afgeleide klassen maken zonder zorgen te maken over het verpotten van de staatsmachine.

Wat betreft het argument dat u geen openbare virtuele methoden moet hebben, zeg ik BS. Gebruikers kunnen net zo gemakkelijk privéviruals overschrijven, net zo gemakkelijk als publieke – ze definiëren tenslotte nieuwe klassen. Als het publiek geen bepaalde API zou moeten wijzigen, maakt het helemaal niet virtueel in openbaar toegankelijke objecten.

Other episodes