Övriga byggstenar Beroenden Pekare till funktioner Övriga byggstenar Beroenden er Definitioners synlighet Funktionspekare Icke-medlemsfunktioner Medlemsfunktioner 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 2 Några tips under programutveckling - att spara tid Minska utveckligstiden: Minimera antal beroenden mellan filerna Skapa ett projekt / använd make. Automatisering lönar sig alltid! Använd debuggern flitigt Undvik dynamisk allokering Undvik att läcka eventuellt dynamiskt minne genom att matcha allokering i konstrukor med deallokering i destruktor. Några tips under programutveckling - att spara tid Minska utveckligstiden: Använd const på rätt sätt ändringar i efterhand kan bli mycket omfattande const hjälper dig att hitta fel Bryt ut gemensam funktionalitet och lägg i rätt klass. Tänk på vad som hör / inte hör hemma i basklassen 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 3 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 4 2D1387 Programsystemkonstruktion med C++ 1
Beroenden För att minska kompileringstiderna vill man ha så få beroenden som möjligt mellan filer: Undvik att inkludera onödiga filer i dina headerfiler. Undvik att använda inline på funktioner som använder annat objekt eftersom dess definition då måste vara synlig. Använd framåtdeklaration där detta är möjligt. När kan man använda framåtdeklaration av en klass och när måste definitionen vara synlig? Definitionen måste vara synlig för alla basklasser Definitionen måste vara synlig då man definierar objekt, t.ex. som medlem Definitionen måste vara synlig då man använder objekt, t.ex. använder en av objektets funktioner 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 5 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 6 Definitionen behöver ej vara synlig i övriga fall! Exempel: Pekare eller referens till objekt Returtyp i deklaration av funktion Argument i deklaration av funktion Användning av typen i typdeklaration eller som malltyp Exempel på framåtdeklaration av klasser // i filen A.h #include "D.h" // inkludera D:s definition class B; // (framåt)deklaration av B class C; // (framåt)deklaration av C class A // definition av klassen A B foo(b); // B behöver ej vara definierad! C *bar(); // C behöver ej vara definierad B &b; // B behöver ej vara definierad D c; // ok: D är definierad B b; // fel: B måste vara definierad std::vector<b> v; // B behöver ej vara definierad 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 7 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 8 2D1387 Programsystemkonstruktion med C++ 2
Klassdefinitionen måste vara synlig vid användning // i filen A.cpp #include "A.h" // A måste vara definierad #include "B.h" // B måste definieras eftersom // foo() använder och returnerar B extern C *get_cp(); // get_cp definierad i annan fil B A::foo(B) return B(); // B måste vara definierad } C *A::bar() C *p = get_cp(); // returnerar pekare till C return p; // C behöver ej vara definierad } // om vi inte använder C Undvik beroenden pimpl-idiomet Pointer to implementation Undvik beroenden i headerfiler genom extra indirektion genom pekare Fördelar: få beroenden!, stora headerfiler (typ windows.h) kan ofta helt elimineras från dina headerfiler Nackdelar: extra avreferering, extra objekt + dynamisk allokering 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 9 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 10 Undvik beroenden // i fil.h class Impl; class A public: A(); ~A(); int foo(); private: Impl *impl; // framåtdeklaration // definieras i fil.cpp // implementeras i Impl // pekare till implementation // i fil.cpp class Impl // här definieras Impl! //...och ingen kommer åt den public: B hidden1; // dessa medlemmar låg förut i A C hidden2; //...vi gömmer dem i Impl! int foo() return 0; } // utför A::foo()s arbete Funktionspekare Pekare till funktioner Pekare till medlemsfunktioner A::A() impl = new Impl; } // allokera A::~A() delete impl; } // deallokera int A::foo() return impl->foo(); } // skicka vidare 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 11 2D1387 Programsystemkonstruktion med C++ 3
Pekare till funktioner Antag att vi har en lista och vill använda eller manipulera varje element En funktionspekare ger oss en möjlighet att slippa upprepa kod Vi applicerar funktionspekaren iterativt, dvs på vart och ett av elementen i listan Pekare till funktioner typedef void (*fp)(int &); // funktionspekare void iterate(int a[], int size, fp action) for(int j = 0; j < size; j++) action(a[j]); } void mod3(int &i) i %= 3; } // rest vid division void print(int &i) std::cout << i << " "; }... int a[7] = 1, 2, 3, 4, 5, 6, 7 iterate(a, 7, mod3); iterate(a, 7, print); // ger utmatning 1 2 0 1 2 0 1 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 13 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 14 Pekare till medlemsfunktioner Precis på samma sätt som man pekar på globala funktioner kan man peka på funktioner i klasser Dessa funktionspekare är lite speciella eftersom de måste ha tillgång till en thispekare Pekare till medlemsfunktion är egentligen en offset som pekar ut den funktion som ska köras Pekare till medlemsfunktioner class A public: void foo(int) } static void bar(int) } typedef void(*fp)(int); fp p1 = &A::foo; // fel: foo är void(a::*)(int) fp p2 = &A::bar; // ok: bar är void(*)(int) typedef void(a::*fpm)(int); fpm p3 = &A::foo; // ok: foo är void(a::*)(int) A a; (a.*p3)(4711); // kör foo från a A *pa = &a; (pa->*p3)(4711); // kör foo från a via pa 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 15 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 16 2D1387 Programsystemkonstruktion med C++ 4
Typinformation och konvertering Implicit och explicit konvertering Säker typkonvertering till nedärvd klass Man använder typkonvertering för att konvertera en typ till en annan Implicit konvertering, dvs automatisk, sker mellan de flesta inbyggda typer Pekare kan konverteras till voidpekare, pekare kan även konverteras till en basklasspekare Subklasser konverteras till basklassen 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 18 Olika typer av explicit konvertering: static_cast är motsatsen till implicit konvertering dynamic_cast är konvertering från basklass till subklass och kan bara lösas under körning reinterpret_cast är bitvis tolkning const_cast tar bort kvalifieraren const eller volatile const char str[] = "C++"; void *vp; vp = str; // fel: kastar bort const vp = (void *)str; // ok, men fult och otyligt vp = const_cast<char *>(str); // rätt sätt char *s = const_cast<char *>(str); // ok vp = s; // ok: samma kvalifierare s = reinterpret_cast<char *>(vp); // ok s = reinterpret_cast<char *>(str); // fel: const_cast // måste användas 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 19 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 20 2D1387 Programsystemkonstruktion med C++ 5
struct A virtual void f() } struct B : public A void f() } B b; A *ap = &b; B *bp = dynamic_cast<b *>(ap); extern void foo(double); foo(7); // bp!= 0 eftersom // ap pekar på b // implicit konvert // från int till dbl Typinformation under körning Genom operatorn typeid() får man en referens till en instans av klassen type_info som innehåller typinformation type_info har en metod name() som ger klassens namn int i = static_cast<int>(3.14); // explicit konvert // från dbl till int 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 21 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 22 Typinformation under körning Exempel på användning: class A A *a = new A; const type_info &ta = typeid(a); // typinfo på klass const type_info &ta = typeid(*a); // typinfo på objekt std::cout << ta.name() << std::endl; // namn på typ if(ta == ta) // typer kan jämföras std::cout << "They are the same" << std::endl; if(ta.before(ta)) // typer har en ordning std::cout << "ta is before ta" << std::endl; 2D1387 Programsystemkonstruktion med C++ Johnny Bigert 2002-2003 23 2D1387 Programsystemkonstruktion med C++ 6