6.
Typkontroll vid programkörning.
· Bakgrund.
· Operatorn ‘typeid’.
· Klassen ‘type_info’.
Bakgrund.
Typkontroll vid programkörning (run-time type information, RTTI) är en mekanik som möjliggör att man kan ta reda på vilken typ/klass ett visst objekt är baserat på vid själva programkörningen. Detta är ett senare tillägg till C++ framtvingat av att många klasstillverkare hade egna lösningar på problemet, vilket orsakade diverse kompatibilitetsproblem mellan olika klassbibliotek. Bara faktum att de gjorde så visade på att behovet av RTTI fanns.
Det är väl att märka, att denna diskussion om RTTI nästan uteslutande gäller i samband med användning av pekare. Konceptet som sådant gäller dock även referenser.
Diskussionen om RTTI i C++ gäller i huvudsak tre områden:
· Operatorn för ‘dynamic_cast’. Används vid typomvandling av polymorpha
typer. Se ‘dynamic_cast Operator’ i hjälpen för ytterligare information.
· Operatorn ‘typeid‘, vilken används för att identifiera typen för ett
objekt.
· Klassen ‘type_info‘, vilken används för att ta emot den typinformation
som ‘typeid‘-operatorn returnerar, utlämna informationen i form av en
textsträng, samt att fungera som jämförelseobjekt.
Operatorn ‘typeid’.
Operatorn ‘typeid’ avgör vilken typ ett objekt
har vid programkörningen. Syntaxen, som liknar den för ‘sizeof’ är följande:
typeid(<uttryck>)
typeid(<typ>)
En ‘typeid’ returnerar en referens till en ‘const type_info’, vilken innehåller antingen angiven typ eller typen för det uttryck som angivits, bereoende av vilken av ovanstående syntaxer som använts. Vi ska strax titta mer på klassen ‘type_info’.
Det vanligaste ‘<uttryck>‘ som kan tänkas förekomma i den första syntaxen är en pekare som pekar på ett objekt. Man kan med den första syntaxen t.ex. få reda på vad objektet som pekaren pPerson pekar på är för typ av objekt:
typeid(*pPerson)
Den andra syntaxen returnerar den typ man anger. Följande syntax returnerar alltså en referens till ett objekt klassen ‘type_info’ innehållande informationen att typen är ‘class CSaljare’:
typeid(CSaljare)
Detta kan verka onödigt vid ett första ögonkast, men är högst användbart när man jämför typer (som vi ser i nästa avsnitt är operatorerna ‘==‘ och ‘!=‘ överlagrade i detta syfte). Man kan t.ex. avgöra om ett objekt som pekas ut av pekaren pPerson inte är av typen CSaljare med följande syntax:
if(typeid(*pPerson) != typeid(CSaljare))
{
cout << "Detta är ingen säljare!\n";
}
Den första syntaxen kan även användas på en pekare i stället för det pekaren pekar på:
typeid(pPerson)
Här får man reda på vilken typ pekaren pPerson är, och i vårt fall är den deklarerad som CPerson-pekare, och resultatet blir en referens till ett objekt av typen ‘class type_info’ innehållande informationen ‘class CPerson’.
Observera att vi måste referera till objektet via en basklasspekare om detta ska få någon mening.
Vi kan ju titta på ett exempel:
class CPerson { ... };
class CJobbare : public CPerson {
... };
void funk()
{
CJobbare *pJobbare = new CJobbare;
CPerson *pPerson = pJobbare;
const
type_info& TypInfo1 = typeid(pPerson);
// TypInfo1 innehåller nu
// informationen ”class CPerson”.
const
type_info& TypInfo2 = typeid(*pPerson);
// TypInfo2 innehåller nu
// informationen ”class CJobbare”.
}
Om pekaren har värdet noll sker ett s.k. ‘exception’ av typen ‘bad_typeid’, d.v.s. felhanteringen träder i kraft med denna typ. Om pekaren inte pekar på ett objekt sker detsamma, men med typen ‘__non_rtti_object’.
Om ‘<uttryck>‘ inte är pekare mot eller referens till en basklass för objketet blir resultatet i ‘type_info’-referensen en statisk typ motsvarande uttrycket. Det var det vi hade ovan:
typeid(CSaljare)
Ett annat sätt att lösa typkontrollen vi hade på sidan 3 skulle kunna vara detta:
const type_info
&TypInfo = typeid(*pPerson);
char *typ =
(char*)TypInfo.name();
if(strcmp(typ,
"class CSaljare"))
{
cout << "Detta är ingen säljare!\n";
}
Här finns alltså klassnamnet att tillgå via pekaren ‘typ’.
Klassen ‘type_info’.
Klassen ‘type_info’ beskriver typen för det objeket som efterfrågats via typeid(). I klassen lagras en pekare mot en textsträng som i sin tur innehåller typnamnet i klartext. Det finns även en motsvarighet för det s.k. ‘dekorerade’ typnamnet. I klassen lagras även en kodad version av typen i avsikt att möjliggöra jämförelser mellan typer. Med samma avsikt finns överlagrade operatorer för ‘==‘ och ‘!=‘.
Vill man ha tillgång till detta måste man ta med typeinfo.h, där klasshuvudet finns beskrivet:
class type_info
{
public:
virtual ~type_info();
int operator==(const type_info& rhs)
const;
int operator!=(const type_info& rhs)
const;
int before(const type_info& rhs)
const;
const char* name() const;
const char* raw_name() const;
private:
...
};
Observera att de överlagrade operatorerna tar ‘const type_info&’ som argument, d.v.s. en referens till en ‘type_info’. Det innebär att det inte fungerar att skriva:
// Fel!!!
if(typeid(*pPerson)
!= "class CSaljare")
{
cout << "Detta är ingen säljare!\n";
}
Lägg även märke till att typinformationen för en klass innehåller nyckelordet ‘class’ före klassnamnet.
Typinformation genereras för polymorfa klasser endast om kompilering sker med flaggan /GR. Denna anger man på följande sätt: Välj [Build - Settings...] eller tryck Alt + F7. I Dialogrutan ‘Project Settings’ som då visas väljer du fliken ‘C/C++’. Vidare väljer du ‘C/C++ Language’ i kombinationsrutan ‘Category’. Nu är vi framme: kryssa för rutan ‘Enable Run-Time Type Information (RTTI)’. Lägg märke till att att flaggan /GR dyker upp i textsträngen i nedersta textrutan.
? Run-Time Type Information.
Övningsuppgift:
· Öppna projektet Firma om det inte redan är öppet.
· Ändra i den rutin som matar in provision för en säljare på så sätt att du testar om det är en säljare som angivits efter det att sökning i listan fått träff på det namn eller det anställningsnummer du angivit.
· Om det inte är en säljare ska du inte anropa SetSaljTotal(), utan i stället skriva ut en text som upplyser att personen inte har någon provision.
· Glöm inte ‘#include <typeinfo.h>‘!
· När du är klar med uppgiften kan du radera projektet, vi kommer inte att behöva det mer.