Typer av ärvning

Den typ av ärvning vi hittills använt är den normala public ärvningen. Typen av ärvning specificeras som tidigare nämnts då klassen definieras, t.ex.:

class Foo : public Bar {
  ...
};

Det finns dock andra typer av ärvning än enbart public. De två andra är private och protected. Typen av ärvning påverkar hur metoder och datamedlemmar är tillgängliga för subklassen samt externa entiteter com använder objekt av klassen.

public ärvning

Detta är den normala formen av ärvning. Alla metoder som är definierade som public i basklassen är public även för subklassen. Data som är deklarerat som private i basklassen kan inte accesseras av subklasser eller externa entiteter. I princip ändras inget från hur det är definierat i baskalssen.

private ärvning

Denna typ av ärvning är den mest restriktiva formen av ärvning. Den gömmer alla detaljer av basklassen för externa entiteter. Om vi t.ex. ärver klassen Circle privat av klassen Shape enligt:

class Circle : private Shape {
  ...
};

då kan ingen som använder objekt av klassen Circle referera till metoden origin(), eftersom det nu är en privat metod i Circle. Privat ärvning kan användas som en form av has-a förhållande. Eftersom ingen kan referera till basklassen är det i princip samma sak som om klassen hade haft en dylik datamedlem. Denna typ av ärvning är den minst använda.

protected ärvning

Detta är en relativt speciell typ av ärvning. Den är designad för att göra ärvning lätt genom att tillåta basklasser att accessera vissa metoder och medlemmar, medan externa entiteter inte tillåts det. Om vi t.ex. ser på vår Shape klass igen märker vi säkert förr eller senare att medlemmen m_Origin som innehåller positionen för figuren används ofta av subklasser. Dessa måste varje gång anropa metoderna origin() och setOrigin() för att accessera denna information. Det vore praktiskt om subklasser istället direkt kunde manipulera m_Origin på något sätt. Ett elternativ är att göra den en public medlem och sedan ärva Shape public, men då kan vem som helst manipulera m_Origin, vilket inte är bra. Om vi istället gör medlemmen protected kan vi använda oss av ett alternativt och bättre sätt. Vi kan då skriva Shape på detta sätt:

class Shape {
public:
  // konstruktor
  Shape (const Coordinate & Origin);
  
  // virtuella metoder för area och omkrets
  virtual float area () const;
  virtual float circum () const;

  // position för figuren
  const Coordinate & origin () const;
  void setOrigin (const Coordinate & Origin);

protected:
  // position
  Coordinate m_Origin;
};

Enda skillnaden är flaggan protected. I praktiken kan subklasser nu accessera m_Origin direkt utan att använda de två accessmetoderna, medan externa klasser och funktioner ännu är begränsade till att använda accessmetoderna. Alla subklasser av subklasser o.s.v. kan accessera m_Origin så länge som ärvningen hela tiden är public eller protected. Man kan alltså även ärva protected förutom att ha dataskyddsnivån protected. Det kan verka en aning klurigt, och det är det tyvärr också.

Om man ärver en klass som protected blir alla metoder och all data som varit public till protected, medan all data som varit protected i baskalssen förblir det i subklassen. Man använder skyddat arv för att införa begränsingar på vad utomstående kan accessera, medan subklasser ännu har full tillgång till allt som varit skyddat som public eller protected i basklassen. För en utomstående är alltså protected arv detsamma som private. Det krävs en aning tankearbete innan man fullt förstår hur protected arv och dataskydd fungerar.

Man kan göra en basklass abstrakt (såsom abstrakta baskalsser med virtuella metoder) genom att göra dess konstruktor protected. På så vis kan ingen utomstående accessera konstruktorn, och således ej heller instantiera klassen. Subklasser däremot har full tillgång till konstruktorn och kan anropa den. På så vi kan man instantiera subklasser.

Sammanfattning

Följande tabell sammanfattar hur de olika skyddsnivåerna påverkar de olika arvstyperna.

Tabell 15-1. Skyddsnivåer och arvstyper

 public ärvningprotected ärvningprivate ärvning
public medlemmar blirpublic medlemmar i ärvda klassenprotected medlemmar i ärvda klassenprivate medlemmar i ärvda klassen
protected medlemmar blirprotected medlemmar i ärvda klassenprotected medlemmar i ärvda klassenprivate medlemmar i ärvda klassen
private medlemmar blirej tillgängliga i ärvda klassenej tillgängliga i ärvda klassenej tillgängliga i ärvda klassen