C++-manual
Förutgående | Kapitel 17. Mera om klasser | Nästa

# Statiska metoder och medlemmar

Då vi hittills definierat klasser har vi för det mesta även haft en eller flera datamedlemmar i klasserna. Dessa datamedlemmar finns sedan instantierade i ett objekt. Varje objekt har en egen version av varje medlem. Inget data är delat mellan olika instanser av samma objekt. I vissa fall vore det praktiskt om alla instanser av samma klass kunde dela samma data så att man t.ex. inte behöver flera instanser av en minneskrävande klass. Ett bra exempel då man vill ah en delad variabel är när man vill ge en unik id till alla instanser av en viss klass. Vi kan t.ex. titta på klassen `Product`:

    #include <iostream>
    #include <string>

    class Product {
    public:
      // konstruktor
      Product (const string & Name, int Id) { m_Name = Name, m_Id = Id; }

      // skriv ut produkten på skärmen
      void print () const;

    private:
      string m_Name;
      int    m_Id;
    };

    void Product::print () const {
      cout << "Produktnamn: " << m_Name << ", id: " << m_Id << endl;
    }

    int main () {

      Product * Lager [10];
      int Id = 0;
      string Names [10] = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};

      // skapa nya produkter
      for ( int Index = 0; Index < 10; Index++ ) {
        Lager[Index] = new Product ( Names[Index], Id++ );
      }

      // skriv ut vårt lager
      for ( int Index = 0; Index < 10; Index++ ) {
        Lager[Index]->print ();
      }
    }

Vi använder en lokal variabel `Id` i `main()` för att numrera våra produkter. Egentligen är denna id produktspecifik data, och borde egentligen vara i klassen `Product`. Man hur vi än försöker kan inte klassen `Product` veta vilken som är den senaste använda id:n. Lösningen är att skapa en statisk variabel i klassen `Product` som innehåller den senaste id:n. På så vi kan varje instans som skapas läsa denna variabel, kopiera värdet till sin medlem `m_Id` och inkrementera den statiska variabeln. En statisk variabel deklareras med nyckelordet `static`. Vi modifierar `Product` en aning:

    #include <iostream>
    #include <string>

    class Product {
    public:
      // konstruktor
      Product (const string & Name);

      // skriv ut produkten på skärmen
      void print () const;

    private:
      // en statisk variabel
      static int m_NextId;

      // normala medlemmar
      string m_Name;
      int    m_Id;
    };

    Product::Product (const string & Name) {
      m_Name = Name;
      m_Id = m_NextId++;
    }

    // initialisera vår statiska variabel
    int Product::m_NextId = 0;

    void Product::print () const {
      cout << "Produktnamn: " << m_Name << ", id: " << m_Id << endl;
    }

Vi har nu en privat statisk variabel. den är privat så att inte externa entiteter skall kunna modifiera nästa id, och på så vis eventuellt skapa produkter med samma id. Man måste ge ett initialvärde åt statiska medlemmar, men det kan inte göras inom `class`-definitionen. Man måste då minnas att en `class` endast är en ritning för hur egentliga instanser skall se ut, och inte reservera minne åt medlemmar eller något sådant. Så minne för `m_NextId` kan inte reserveras inne i klassen, det görs en gång (och endast en gång) då den första instansen av klassen skapas. Initialiseringen sker alltså endast en gång! Vi kan sedan använda variablen inne i klassens metoder som vilken medlem som helst, bara vi minns att om vi ändrar på dess värde syns samma ändring i alla andra instanser av klassen. Vi kan nu modifiera vår `main()` så den använder den nya klassen:

    int main () {
      Product * Lager [10];
      string Names [10] = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};

      // skapa nya produkter
      for ( int Index = 0; Index < 10; Index++ ) {
        Lager[Index] = new Product ( Names[Index] );
      }

      // skriv ut vårt lager
      for ( int Index = 0; Index < 10; Index++ ) {
        Lager[Index]->print ();
      }
    }

Nu har vi lyckats förflytta den för `main()` irrelevanta hanteringen av id:n till `Product`.

Statiska medlemmar förstörs då programmet terminerar. Trots att vi skulle radera varje instans av vår `Product`-klass skulle `m_NextId` ändå hålla samma värde.

## Statiska metoder

Förutom endast statiska medlemmar i klasser och variabler i funktioner kan man även ha statiska metoder i klasser. Vad gör då en statisk metod? En statisk metod är en metod som tillhör klassen med som inte tillhör något instantierat objekt. Den kan inte accessera normala medlemmar som hör till instanser av klassen, utan endast statiska medlemmar. Som exempel kan vi göra en statisk metod till `Product` som skriver ut vad nästa id skall vara:

    class Product {
    public:
      ...

      // statisk metod
      static void printId () { cout << "Nästa id: " << m_NextId << endl; }

      ...
    };

En statisk metod definieras genom att placera nyckelordet `static` framför metodens returvärdesdefinition. Metoden kan implementeras antingen som vi gjort ovan direkt inline eller så på normalt sätt:

    void Product::printId () {
      cout << "Nästa id: " << m_NextId << endl;
    }

Då man använder metoden kan man inte referera till den via ett objekt, utan direkt via klassen:

    Product P ("Foo");
    P.printId ();        // fungerar inte!
    Product::printId (); // korrekt anrop

## Singleton

Vad har man då för nytta av statiska metoder? I fallet ovan är nyttan klar, men i övrigt finns det inte så väldigt många situationer där statiska metoder är nödvändiga. Ett lysande undantag är en Singleton-klass. En dylik klass är en klass som man vill att skall finnas i endast en instans i ett program. Det kan t.ex. vara frågan om en klass som abstraherar en resurs som det finns nedast en av. Istället för att skapa en klass och placera den någonstans så att alla andra klasser som behöver den kan komma åt den, kan man göra den till en Singleton. Det är ett väldigt enkelt och elegant sätt att uppnå en lösning på problemet. Läs mera om Singletons och andra praktiska metoder i Design Patterns (se Referenser).

Förutgående | Hem | Nästa
Standardvärden för funktioner och metoder | Upp | Pekaren `this`