4. Mer om klasser

 

 

·     Statiska medlemmar.

·     Statiska metoder.

 

·     Vänner emellan.

 

·     Funktion som vän.

 

·     Objektlistor.

 

·     _set_new_handler.

 

·     Överlagring av ‘new’ och ‘delete’.

 

·     Klasspecifika ‘new’ och ‘delete’.

 

 

 

 

 

 

 

 

 

 

 


Statiska medlemmar.

 

Det finns situationer när man har en variabel som man behöver kunna byta värde på, samtidigt som det ska ha samma värde i alla objekt som skapats med den klass där variabeln deklarerats. Ta t.ex. ett konto i en bank, vi gör en klass som gäller för just denna typ av konto. För var och en som har ett sådant konto skapar vi ett separat objekt. Var och en kan ha sitt saldo etc. Men de ska alla ha samma ränta, ty detta är samma typ av konto, och ändå kan räntan ändras.

 

Då tänker den erfarna C-programmeraren genast i globala banor. Deklarera räntan globalt, så löser vi det problemet. Men vi bryter också mot filosofin i objektorienterad programmering. Dels ingår inte räntan i objektet längre, dels har vi förlorat inkapslingen. Vilken funktion som helst har nu tillgång till att läsa och ändra räntan. Man löser det i stället genom att deklarera variabeln globalt, men ange att den tillhör klassen genom att använda klassnamnet och räckviddsoperatorn. Detta måste naturligtvis göras efter klassbeskrivningen.

 

Hur kommer då ‘static’ in i bilden? När vi normalt deklarerar en statisk variabel i en funktion betyder det att den hela tiden finns i minnet, och att den behåller sitt värde hela programkörningen igenom. Hur skulle detta se ut om vi deklarer­ade en statisk medlem i en klass? Det skulle ju vara ganska meningslöst, efter­som medlemmarna ändå existerar så länge objektet ‘lever’.

 

För att det ska få någon mening som åtminstone liknar den vi redan sett har man definierat följande: När en medlemsvariabel deklareras med nyckelordet ‘static’ kommer den att finnas i endast en kopia i minnet, tillgänglig för alla objekt skapade med klassen som mall, oavsett hur många objekt som skapats. Det går alltså inte att deklarera medlemsvariabeln som static utan att den finns som globalt deklarerad variabel. Detta genererar ett länkfel. Man kan t.ex. de­kla­rera en kontoklass så här:

 

class CKonto

{

public:

    CKonto(char *k, float s = 0.0F); // Bör inte ha med m_Ranta

    static void SetRanta(float r);   // i argumentlistan!

    static float GetRanta();

    ~CKonto();

private:

    char m_Kontonummer[11];

    float m_Saldo;

    static float m_Ranta;      // Måste även finnas som global!

};

 

float CKonto::m_Ranta = 0.0F;  // Global variabel till CKonto.

 

Man bör inte låta denna medlem ingå i konstruktorns argumentlista. Det skulle ju innebära att man ändrar räntan för alla konton när man registrerar ett nytt. Fak­tum är att man helst inte ska använda accessfunktioner från objekten, efter­som detta ser ut som om man endast avser det objekt man anger. I stället kan man använda klassnamnet och räckviddsoperatorn för att anropa accessfunk­tion­erna:

 

CKonto::SetRanta(12.5F);

 

Observera att detta är möjligt endast om SetRanta() är deklarerad som ‘static’. Samma gäller naturligtvis GetRanta(). Här har vi i alla fall vår motsvarighet till global variabel. Den uppträder helt enkelt som en global variabel för alla objekt som skapats med denna klass som mall.

 

Dataintegriteten bevakas genom att vi deklarerat m_Ranta under ‘private’. Man kan annars lätt tro att det skulle gå att skriva:

 

CKonto::m_Ranta = 12.5F;

 

Detta genererar dock detta felmeddelande:

 

error C2248: 'm_Ranta' : cannot access private member in class

‘CKonto’

 

Övningsuppgift:

·      Skapa ett projekt konto, med klassen CKonto enligt ovanstående.

·      Lägg till en utskriftsfunktion ‘Rapport()’ i CKonto. Rapport() ska skriva ut kontonummer, ränta och saldo för ett objekt och avsluta med nyradstecken.

·      Skapa två objekt i main(), med olika kontonummer och saldo, räntan blir då 0.0%.

·      Anropa Rapport() för att se hur det ser ut.

·      Sätt räntan till 12.5 %.

·      Anropa Rapport() för bägge objekten, för att se att räntan ändrats i båda två.

·      Skapa ett tredje konto.

·      Anropa Rapport() och se att räntan inte återställts till 0.0%.

 

 

 

 

 

 

 

 

? Visual C++ Books - C/C++ - iostream Reference - iostream Programming - Using Insertion Operators and Controlling Format.


Statiska metoder.

 

Statiska metoder är metoder speciellt avsedda att arbeta med statiska medlem­mar. Man får deklarera en metod som ‘static’ om den endast använder statiska medlemmar.

 

När man annars har deklarerat sin statiska medlem skulle den ju kunna ändras från vilket ob­jekt som helst. Då uppstår genast frågan, vilket ska jag välja? Gör det detsamma känns det väldigt omotiverat att välja ett. Programkoden skulle se ut som om man hade olika m_Ranta i olika objekt. Dessutom kanske det är svårt om alla objekt är dynamiskt skapade, d.v.s. vi har inte något självklart namn.

 

Ska man då skapa ett ‘dummy’-objekt att ändra räntan ifrån, eller hur ska jag ändra räntan utan att skriva något som förvirrar den som eventuellt ska ändra i programmet vid ett senare tillfälle.

 

Som vi såg i föregående avsnitt kan man lägga till nyckel­ordet ‘static’ när man de­klarerar metoden. Observera att detta låter sig göras endast om metoden bara använder statis­ka medlemmar.

 

Att inte utnyttja detta, utan i stället påverka medlemmen direkt vore inte mycket bättre än att använda en global variabel. Vi bör alltså inte kunna utnyttja detta:

 

CKonto::m_Ranta = 12.25;

 


Vänner emellan.

 

Inkapslingen skyddar vårt data och låter klasskonstruktören behålla händerna fria att förändra klassen även sedan den börjat användas. Vill man lägga till funktionalitet som hör till klassens data måste man lägga det i samma klass. Detta kan resultera i att klassen blir oöverskådligt stor, varför man lagt till be­greppet ‘friend classes’. Följande regler gäller:

 

·     En klass som deklarerats som ‘friend’ till en annan klass har tillgång till den andre klassens privata medlemmar.

·     Man deklarerar i en existerande klass att en senare deklarerad klass kommer att vara dess ‘friend’.

·     Man kan inte deklarera en ny klass som ‘friend’ till en existerande klass om man inte i den existerande klassen i förväg deklarerat att den kommer att vara ‘friend’ till den nya klassen.

·     Det är alltså den klass som innehåller nyckelordet ‘friend’ som ger den andre klassen tillgång till sina variabler. Har man inte källkoden kan man fortfarande inte komma åt privata medlem­mar. Dataintegriteten är fortfarande säkrad.

 

Vi ska som vanligt demonstrera detta med ett mycket enkelt exempel. Det är så enkelt att vi egentligen kunde gjort allt i en och samma klass, men då skulle det ju inte visa meningen med vänskapen. Vi får föreställa oss vikten av att göra på motsvarande sätt när vi har större klasser (vilket vi får i Windowsprogram).

 

Vi gör en telefonlista med en vänklass med tilläggsfunktioner som kan användas när man vill göra en lista på telefonlistans innehåll. Om man jämför detta med Windowsprogram så har vi en klass för själva data, och en klass för det vi ser på listan. Windowsprogrammen kommer vi senare att dela upp på ett liknande sätt, data i CDocument och bildskärmshan­teringen i CView.

 

 

#include<string.h>

#include<iostream.h>

 

// Struktur för namn och nummer.

struct Post

{

    char m_Namn[30];

    char m_Nummer[16];

};

const int MAXANTAL = 100;

 


// Klass för själva listan, innehåller inkapslat data.

class CTeleLista

{

    friend class CTeleFunktioner;

public:

    CTeleLista();

    int Ny(const Post &nypost);

    Post *Leta(char *namn);

private:

    Post lista[MAXANTAL];

    int LedigPosition;

};

 

// Konstruktor.

CTeleLista::CTeleLista()

{

    LedigPosition = 0;

}

 

// Funktion som lägger till ny post.

int CTeleLista::Ny(const Post &nypost)

{

    if(LedigPosition < MAXANTAL)

    {

        lista[LedigPosition++] = nypost;

        return 1;  // För att visa att det gick bra.

    }

    else return 0; // Det gick inte att lägga till fler.

}

 

// Funktion som letar upp angiven post efter namn.

Post *CTeleLista::Leta(char *namn)

{

    for(int i = 0; i < LedigPosition; i++)

    {

        if(!strcmp(lista[i].m_Namn, namn))

        {

            return &lista[i];

        }

    }

    return 0;

}

 

// Klass för extra funktioner:

class CTeleFunktioner

{

public:

    CTeleFunktioner(CTeleLista &tele);  //Obs! Tar listobjektet

    Post *GetFirst();                  //som argument.

    Post *GetLast();

    Post *GetNext();

    Post *GetPrev();

private:

    // Pekare avsedd för en CTeleLista (kompisen)

    CTeleLista *const m_telelista;

    int m_AktuellPosition;

};

 

 

Observera att när vi deklarerar en pekare avsedd att peka på ett objekt så skapas ett tomt objekt i minnet!

 

Vi har just nu ingen tillåten plats att initiera den medlem som är avsedd att peka på ett objekt av vänklassens typ. Det är inte tillåtet i klassbeskrivningen, ej hel­ler i konstruktorns kod. Därför finns det en ny syntax. Konstruktorn använder samma syntax för att initiera en konstant medlem som pekar på ett objekt av vän­klassens typ till medlemsvariabeln som när man passerar ett argument vidare till en förälderklass:

 

CTeleFunktioner::CTeleFunktioner(CTeleLista &tele):m_telelista(&tele)

{

    m_AktuellPosition = 0;

}

Post *CTeleFunktioner::GetFirst()

{

    m_AktuellPosition = 0;

    return &(m_telelista->lista[m_AktuellPosition]);

}

Post *CTeleFunktioner::GetLast()

{

    m_AktuellPosition = m_telelista->LedigPosition - 1;

    return &(m_telelista->lista[m_AktuellPosition]);

}

Post *CTeleFunktioner::GetNext()

{

    if(m_AktuellPosition < m_telelista->LedigPosition - 1)

    {

        m_AktuellPosition++;

        return &(m_telelista->lista[m_AktuellPosition]);

    }

    else return 0;

}

Post *CTeleFunktioner::GetPrev()

{

    if(m_AktuellPosition > 0 )

    {

        m_AktuellPosition--;

        return &(m_telelista->lista[m_AktuellPosition]);

    }

    else return 0;

}

 


Nedan finns en global funktion som använder CTeleFunktioner till att skriva ut alla poster på skärmen. Funktionen tar en referens till CTeleLista-objektet som argument, skapar ett objekt av typen CTeleFunktioner, och använder dess funktioner för att hämta uppgifter i CTeleLista-objektet. Om man i framtiden skulle bygga om klasserna CTeleLista och CTeleFunktioner så att dessa sparar data på annat sätt kommer vi inte att behöva ändra i varken denna funktion eller i main(), förutsatt att gränssnittet hålls intakt.

 

void SkrivLista(CTeleLista &inlista)

{

    Post *aktuell;

    // ‘alla’ blir vän med ‘inlista’ (=MinaVanner)

    CTeleFunktioner alla(inlista);

    aktuell = alla.GetFirst();

    cout << aktuell->m_Namn << ' '

         << aktuell->m_Nummer << '\n';

    while(aktuell = alla.GetNext())

    {

        cout << aktuell->m_Namn << ' '

             << aktuell->m_Nummer << '\n';

    }

}

 

void main()

{

    // Skapa ett objekt från CTeleLista endast!

    CTeleLista MinaVanner;

 

    // Vi behöver en lokal post för inmatning.

    struct Post p;

 

    // Flagga för avslut.

    int fler = 1;

 

    cout << "Mata in dina vänner, avsluta med 'slut'.\n";

    do

    {

        cout << "\n  Namn: ";

        cin.getline(p.m_Namn, 30);

        if(strcmp(p.m_Namn, "slut"))

        {

            cout << "Nummer: ";

            cin.getline(p.m_Nummer, 16);

            fler = MinaVanner.Ny(p);

        }

        else

        {

            fler = 0;

        }

    }while(fler);

 

    cout << "\nDetta var vad du skrev:\n\n";

    SkrivLista(MinaVanner); //Skapar CTeleFunktioner-objekt

    cout << '\n';

}

 

 

Övningsuppgift:

·      Testa ovanstående lista.

·      Så här kan det se ut när man använder programmet:

 

                       

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Funktion som vän.

 

Man kan även deklarera enstaka funktioner som ‘friend’ till en viss klass i stället för en hel klass. T.ex.:

 

class CDinKlass

{

public:

    CDinKlass();

    friend char *AvenForDig(CDinKlass dk);

    CDinKlass();

private:

    char *m_pKompisar;

    char *m_pAlskarinnor;

};

 

char *AvenForDig(CDinKlass dk) // Fristående funktion!

{

    return dk.m_pKompisar;

}

 

Observera att vi behöver ett objekt av klassens typ för att kunna få tillgång till rätt data. Vi har ju inte deklarerat vänfunktionen i klassen (CDinKlass:: saknas före funktionsnamnet). Sedan använder vi detta objekt för att referera till funk­tionen.

 

Kom ihåg att alla namn i en struktur eller en klass in­ternt lagras som offset under kompileringen, d.v.s. hur många bytes längre fram i minnet man hittar en medlem räknat från objektets startadress.

 

Detta utnyttjas speciellt vid global operatoröverlagring, vilket vi ska titta mer på senare.


Objektlistor.

 

Man kan deklarera listor med objekt p.s.s. som man kan deklarera listor med vilken annan datatyp som helst.

 

CDatum Foedelsedag[10];

 

När man deklarerar en lista med objekt kallas konstruktorn för vart och ett av objekten. Om man gör som ovan, d.v.s. inte initierar objekten, krävs en förvald konstruktor, ‘default constructor’, d.v.s. en konstruktor som inte tar några argu­ment (se föregående kapitel). En sådan konstruktor bör naturligtvis initiera medlemmarna med tillåtet, om ej meningsfullt, data. Vår klass CDatum fick förut en förvald konstruktor som initierade datumobjekten till 1984-01-01. Skulle vi göra ovanstående deklaration fick vi alltså 10 stycken datumobjekt vilka alla hade detta datum.

 

Vill man initiera med ett annat datum går det naturligtvis bra att göra även detta, förutsatt att det finns en lämplig konstruktor. Man använder en syntax där konstruktornamnet ingår:

 

CDatum Foedelsedag[10] = {CDatum(95,07,23),

                          CDatum(83,05,12),

                          CDatum(65,06,30),

                          CDatum(72,09,22),

                          CDatum(88,12,08),

                          CDatum(89,04,01),

                          CDatum(51,11,22),

                          CDatum(77,05,15),

                          CDatum(79,01,19),

                          CDatum(53,10,11)};

 

Om man inte anger lika många initierare som listan är stor kallas ‘default con­structor’ för de överblivna element där initierare saknas. I vårt fall ovan sker detta inte, eftersom listan är 10 lång och vi har angivit 10 initierare.

 

Det är också tillåtet att använda olika initierare, om sådana finns. därigenom skulle vi kunna styra vilka element som får vilken initierare, även den förvalda. Den förvalda kallas med tomma paranteser. Detta skulle vi kunna testa på vår klass Ctext: (Observera att vi anger paranteser för förvald konstruktor!)

 

CText Texter[4] = {CText(),

                   CText(”Initierad och klar”),

                   CText(‘+’, 10)};

 

Den första texten använder ‘default constructor’ explicivt, den andra den konstruktor som lägger in angiven text, den tredje får 10 ‘+’-tecken och den fjärde använder ‘default constructor’ implicivt, eftersom den ‘blev över’.

Man kan också kalla en konstruktor som bara tar ett argument utan att ange konstruktorn, implicivt. Skillanden syns i nedanstående exempel:

 

CText Rader[2] = {”Detta är rad ett.\n”,        // implicivt

                  CText(”Detta är rad två\n”)}; // explicivt

 

Observera att ovanstående är ett exempel på impliciv användning av ‘default constructor’. Det är inte den överlagrade operatorn som anropats p.g.a. till­delningstecknet! Det finns inte någon möjlighet att använda den överlagrade tilldelningsoperatorn vid initieringar.

 

Man kan naturligtvis allokera dynamiska objektlistor m.h.a. ‘new’. Nedan­stående exempel skapar och initierar en pekare mot en lista med CTextobjekt, vilka också skapas:

 

CText *pText = new CText[5];

 

Det finns dock inget sätt att initiera objekten i denna syntax. Den förvalda konstruktorn kommer att anropas för alla fem elementen. Observera även att man kan använda en variabel inom hakparanteserna lika väl som en konstant.

 

När man återlämnar minnet är det viktigt att man talar om att man återlämnar minne för en hel lista. Glömmer man detta kommer inte kompilatorn att kunna upptäcka felet, utan tolkar det som att vi vill ta bort ett objekt där pekaren pek­ar! Det blir bara själva listan som tas bort, tillsammans med det första objektet. Det minne som resten av objekten reserverat återlämnas aldrig. Använd därför rätt syntax:

 

delete pText;   // Fel! Endast första objektet förstörs!

delete [] pText // Rätt! Alla objekt tas bort.

 

Anledningen är den att destruktorn ska anropas för alla objekt. Detta sker automatiskt vid den korrekta syntaxen medan den felaktiga bara anropar konstruktorn i det första objektet. Om man använder en klass som inte har någon destruktor är det möjligt att använda den felaktiga syntaxen, men detta avrådes definitivt.

 

I äldre versioner av C++ var man tvungen att ange antalet element inom hak­paranteserna. Kompilatorn kontrollerade dock att rätt antal angivits, och gene­rerade ett fel om så inte var fallet.

 

Övningsuppgift:

·      Öppna projektet Datum.

·      Ändra i main() så att du allokerar en lista med new som innehåller de tre objekten i stället för att använda de pekare som finns sedan tidigare övningsuppgift.

·      I slutet av main använder du ‘delete’ på hela listan för att ta bort objekten.

·      Kompilera, länka och testa. Det ska bara bli ‘1 Januari 1984’ tre gånger.


_set_new_handler.

 

Felhanteringen i malloc() och ‘new’ är enkel. Fick vi inget minne av operativ­systemet returnerar det 0 i stället för en korrekt pekare. God programmering kräver att vi kontrollerar detta, och meddelar användaren att minnet inte räcker. Dålig programmering är att försöka använda pekaren i alla fall, och låta datorn krascha. Observera att just detta har vi gjort genomgående i våra exempel, men i det goda syftet att inte blanda in för många aspekter i varje övningsexempel och därigenom göra dem svårare att förstå.

 

I C++ finns möjligheten att skriva en egen rutin för felhantering av minnesbrist. Funktionen _set_new_handler() anger en av våra funktioner som felhanterare, varvid vi aldrig kommer tillbaks till vårt program med en nollpekare. I stället anropas den angivna funktionen. Där kan man t.ex. meddela användaren att minnet inte räcker längre, och sedan stänga programmet. Sedan kan vi fortsätta skriva våra program som tidigare. Du måste ta med new.h för att få tillgång till _set_new_handler. Ett exempel på ett program som äter upp hela minnet:

 

#include <iostream.h>

#include <stdlib.h>

#include <new.h>

 

int MinnetSlut(size_t storlek)

{

    cerr << "\n\aMinnet räcker inte för denna operation!"

         << "\nProgrammet avbryts!\n";

    exit(1);

    return 0;

}

 

void main()

{

    _set_new_handler(MinnetSlut);

    long Total = 0;

    while(1) // Evig slinga!

    {

        // Allokera mycket minne (100Kb)!

        char *shitload = new char[102400];

        Total += 102400;

        cout << "Nu har vi allokerat " << Total << " bytes.\n";

    }

}

 

Observera att:

·     Operativsystemet återtar minnet som programmet stulit.

·     Vi måste ange ‘int’ som returtyp och en ‘size_t’ sturktur som argument, vilken innehåller storleken på begärt minne.

·     Programmet avslutas i MinnetSlut(), inte i main():s eviga slinga.


Överlagring av ‘new’ och ‘delete’.

 

Man kanske tänker sig ‘new’ och ‘delete’ som funktioner snarare än operatorer, men operatorer är just vad de är. Därför kan vi använda operatoröverlagring även här. Vi har ju redan sett att man kan överlagra tilldelningstecknet, och i ett kommande kapitel kommer vi att se att nästan alla operatorer kan överlagras och ge helt ny betydelse. T.ex. ‘<<‘ och ‘>>‘ är överlagrade och har egentligen betydelsen SHL respektive SHR, d.v.s. shift left respektive shift right. Slå upp dessa i hjälpen och prova själv!

 

I de allra flesta fall klarar man sig med ‘new’ och ‘delete’ som de är, men låt oss skapa ett exempel där vi vill att ‘new’ automatiskt ska fylla en heltalslista med värdet noll. Heltal är inga objekt, och har därför inte konstruktor som kan göra jobbet. Alltså överlagrar vi ‘new’:

 

void *operator new(size_t bytes)

{

    void *adress = calloc(1, bytes);

    return adress;

}

 

Vi behöver stdlib.h och malloc.h för att göra detta. Som synes sker minnesal­lokeringen m.h.a. funktionen calloc(). Slå upp den i hjälpen och förklara siffran 1 i argumentlistan. Observera att ‘new’ tar ett argument av typen size_t, vilken automatiskt initierats av kompilatorn till att innehålla storleken på det objekt som ska allokeras. Returvärdet ska vara en ‘void pointer’!

 

Om vi reserverat minne m.h.a. vår nya ‘new’ vill vi naturligtvis återlämna det m.h.a. ‘delete’. Nu är det bara det att vi egentligen reserverat minnet m.h.a. calloc(), och måste återlämna det med free(). Då är det lämpligt att även över­lagra ‘delete’:

 

void operator delete(void *adress)

{

    free(adress);

}

 

Observera att argumentet måste vara en ‘void pointer’, d.v.s. detsamma som vi fick från ‘new’, medan returvärdet är ‘void’.

 

Om vi överlagrar ‘new’ så hindrar detta inte att konstruktorn anropas om vi anger att ett objekt ska allokeras i stället för en variabel. Vi har också fria händer att utöka antalet argument efter våra speciella behov. I nedanstående övningar ska du testa ovanstående på egen hand, och lägga till ett extra argument i andra övningen.

 

Övningsuppgift:

·      Skapa ett nytt projekt newdel.

·      Skriv in ovanstående överlagringar av ‘new’ och ‘delete’.

·      I main() använder du ‘new’ för att reservera utrymme för 10 heltal (40 bytes blir det, kompilatorn håller reda på det åt dig).

·      Skriv en for-slinga som skriver ut alla heltalen på skärmen, det ska bli 10 nollor.

·      Observera att överlagringen av ‘delete’ får en konflikt med en liknande överlagring som definierats i iostream.h. Använd därför inte den, utan välj att skriva ut med printf() i stdio.h i stället.

 

Övningsuppgift:

·      Öppna projektet newdel om det inte redan är öppet.

·      Lägg till ett argument ‘int fyllnad’ i operatoröverlagringen för ‘new’.

·      Ändra calloc() till malloc().

·      Använd funktionen memset() i operatoröverlagringen för ‘new’ för att fylla det reserverade minnet med det värde som angivits i ‘fyllnad’. Detta får bara göras om malloc() inte returnerade 0!

·      Gör följande ändring i main(): ändra ”... = new int[10];” till
”... = new(‘*’) char[10];”

·      Ändra i utskriften så att den skriver ut typ ‘char’ i stället för typ ‘int’ och skriv ut alla 10 elementen.

 

 


Klasspecifika ‘new’ och ‘delete’.

 

Som du kanske lagt märke till har vi överlagrat operatorer dels i klasser, dels globalt. När vi överlagrade tilldelningsoperatorn ville vi bara att dess betydelse skulle ändras när man använde den tillsammans med objekt av typen CText, men inte för andra typer. När vi överlagrade ‘new’ och ‘delete’ ville vi att det skulle gälla alla typer, och vi förlorade samtidigt tillgången till originalver-sioner­na av dessa operatorer.

 

Vi kan överlagra operatorerna ‘new’ och ‘delete’ i en klass i stället. Då gäller överlagringen endast minnesallokering inom klassens räckvidd, d.v.s. de funktioner som ingår i klassen. Överlagringen gäller fortfarande alla datatyper, det är bara räckvidden som blir begränsad till klassen.

 

Man har fortfarande tillgång till originalversionerna av ‘new’ och ‘delete’ gen­om räckviddsoperatorn:

 

int *p = ::new int; // Använder globala new inifrån ett objekt.

 

Man deklarerar operatoröverlagringarna tillsammans med medlemsmetoderna, precis som vi gjorde när vi överlagrade tilldelningsoperatorn.

 

En mycket viktig aspekt av att överlagra operatorer i klasser är att dessa anropas när man manipulerar objekt skapade med klassen som mall! Betänk att vi kunde skriva:

 

Tomt = Kopia = Original;

 

Detta var tre objekt skapade med klassen CText som mall (se föregånde kapitel). När vi skrev på detta sätt befann vi oss på global nivå, i main(), men använde den överlagrade versionen av tilldelningsoperatorn som finns i klassen CText.

 

Samma sak gäller överlagringen av ‘new’ och ‘delete’. När vi skapar ett objekt med en klass som har överlagrat ‘new’ och ‘delete’ som mall, så används klas­sens överlagrade versioner! Således sker följande när vi skapar ett objekt:

 

·     Överlagrad version av ‘new’ anropas.

·     Konstruktorn anropas.

·     Objektet används...

·     Destruktorn anropas.

·     Överlagrad version av ‘delete’ anropas.


Överkursuppgift:

·      Försök att på egen hand skapa följande projekt ordning.

·      Skriv en enkel klass CNamn vilken innehåller en char-lista m_Namn[25].

·      Konstruktor och destruktor ska endast rapportera att de körs. Använd någonting liknande CGodagAdieu, men utan att objektets namn behöver vara med. (Kapitel 2, ‘Klasser och objekt’, avsnitt ‘Constructor/Destructor’.) Vi kommer bara att använda ett objekt.

·      Överlagra ‘new’ och ‘delete’, ‘new’ ska tala om att den körs, och sedan reservera utrymme för ett objekt av storleken CNamn.

·      Även ‘delete’ ska rapportera att den körs (använd puts).

·      I main() skapar du ett objekt med klassen CNamn som mall, men använd ‘new’!

·      Ta sedan bort objektet m.h.a. ‘delete’.

·      Studera utskriften och jämför resultatet med vad som påståtts här ovan