C++-manual | ||
---|---|---|
Förutgående | Nästa |
Detta kapitel behandlar olika aspekter på hur klasser fungerar i C++. Många små ämnen har här kombinerats ihop till ett kapitel.
Förutom normala primitiva datatyper, sammansatta datatyper och vektorer kan man även allokera objekt dynamiskt. Dynamiskt allokerade objekt skiljer sig väldigt lite från automatiska objekt, d.v.s. objekt som skapas automatiskt av kompilatorn i någon viss funktion eller metod. Fördelarna med att dynamiskt allokera objekt när de behövs är desamma som för andra typer av minne:
mindre slöseri med minne.
programmet är mera dynamiskt i olika situationer.
Allmänt ser en allokering av en klass ut på följande sätt:
klasstyp * variabel1 = new klasstyp; klasstyp * variabel1 = new klasstyp (parametrar); |
Vi antar att vi har en klass dom heter Coordinate definierad (se Kapitel 14). Vi kan allokera en dylik genom följande anrop:
// använd ursprunsgvärden Coordinate * C1 = new Coordinate; // använd egna värden Coordinate * C2 = new Coordinate ( 1, 2 ); |
Vilken metod som används beror på lklassen ifråga och vilka olika konstruktorer denna har. Vår klass Coordinate har både en tom konstruktor och en konstruktor som tar två parametrar. Allokerade objekt måste även förstöras för att inte läcka minne. Det görs som normalt med operatorn delete. För att deallokera ovan allokerade objekt använder vi:
delete C1; delete C2; |
Man bör minnas att det endas sätt att referera till ett allokerat objekt är via en pekare som pekar till objektet. Finns det ingen pekare längre som pekar på objektet är det förlorat för all framtid och kan inte igen användas. Vi har då läckt minne.
För att allokera en vektor av objekt används samma syntax som för primitiva datatyper:
Coordinate * Vector = new Coordinate [42]; |
Man adresserar sedan denna på samma sätt som normala vektorer, d.v.s. via []. En vektor av objekt raderas även på samma sätt som en normal vektor:
delete [] Vector; |
Då objekt allokeras med new och förstörs med delete exekveras deras konstruktorer och destruktorer på normalt sätt.
Ett exempel på dynamisk minneshanterin följer i programmet nedan. Det använder sig även av interna klasser (se avsnittet Klasser i klasser i Kapitel 14) och illustrerar hur dessa kan användas. Programmet implementerar ett mycket enkelt binärt träd som lagrar heltal. Man kan sätta in tal i trädet samt skriva ut trädet. Trädet använder sig av rekursiva hjälpfunktioner för att skriva ut trädet samt lägga in element.
#include <iostream> #include <stdlib.h> #include <time.h> class Tree { public: // konstruktor Tree () : m_Root (0) {}; ~Tree (); // lägg till ett tal void add (const int Value); // skriv ut trädet void print () { print ( m_Root ); } private: // intern klass för att hålla data om en nod class Node { public: Node (const int Value) : m_Value(Value), m_Left(0), m_Right(0) {}; ~Node (); // värdet för noden int m_Value; // vänster och höger subträdt Node * m_Left; Node * m_Right; }; // rekursiv metod för att lägga till ett värde och skriva ut void add (const int Value, Node * Root); void print (Node * Root); // roten för trädet Node * m_Root; }; Tree::~Tree () { // har vi en rot? if ( m_Root ) delete m_Root; } void Tree::add (const int Value) { // har vi en rot redan? if ( ! m_Root ) // nej, så skapa en rot och gå iväg m_Root = new Node ( Value ); else // lägg till värdet till roten add (Value, m_Root); } void Tree::add (const int Value, Node * Root) { // kolla värdet för denna nod if ( Value < Root->m_Value ) { // har vi en gren till vänster? if ( Root->m_Left ) { // jep, rekursera vidare add (Value, Root->m_Left); } else { // nej, så skapa en nod till vänster Root->m_Left = new Node (Value); } } // kolla värdet för denna nod else if ( Value > Root->m_Value ) { // har vi en gren till höger? if ( Root->m_Right ) { // jep, rekursera vidare add (Value, Root->m_Right); } else { // nej, så skapa en nod till vänster Root->m_Right = new Node (Value); } } } void Tree::print (Node * Root) { // har vi ett värde i noden? if ( ! Root ) // nej, gå bort return; // skriv ut vänster subträd print ( Root->m_Left ); cout << Root->m_Value << " "; // skriv ut höger subträd print ( Root->m_Right ); } Tree::Node::~Node () { // har vi ett vänster subträd? if ( m_Left ) delete m_Left; // har vi ett höger subträd? if ( m_Right ) delete m_Right; } int main () { Tree T; // initiera slumptalen srand ( time (0) ); // sätt in 20 slumptal i trädet for ( int Index = 0; Index < 20; Index++ ) { T.add ( (int)((float)rand () / (float)RAND_MAX * 100.0) ); } // skriv ut trädet T.print (); } |
Logiken i programmet är relativt enkel och kräver inga närmare beskrivningar. Notera dock hur destruktorn för den interna klassen Node implementerats. Eftersom den är intern för klassen Tree räcker det inte med enbart:
Node::~Node () { ... } |
utan man måste även berätta att det är en metod för en klass som tillhör Tree genom att även skriva Tree:: enligt:
Tree::Node::~Node () { ... } |
Förutgående | Hem | Nästa |
Multipel ärvning | Friends |