 |
|
 |
 | Run-Time Type Checking C++ |  |
Trimis: 19/07/2007 16:35 |
|
|
| Ad_Infinitum |
| Administrator |

 |
 |
| Data inscrierii: 23/Iun/2007 |
| Mesaje: 212 |
|
|
|
 |
 |
 |
|
Introducere
O intrebare frecvent intâlnitã este "Cum pot afla/verifica tipul unui obiect la run-time?".
Sã luam o problemã simplã:
Sã se completeze codul de mai jos în aºa fel încât la inițialul apel funcþia CFoo::SpuneCevaMaiAnimalule sã afiºeze "Ham! " iar la al doilea sã afiºeze "Miau! ". Cu alte cuvinte, sã se verifice la run-time dacã am transmis ca parametru un pointer la un obiect de tipul Caine sau la Pisica.
| Citat: |
class Animal {...};
class Caine : public Animal {...};
class Pisica : public Animal {...};
class CFoo
{
public:
void SpuneCevaMaiAnimalule(Animal*) {...}
};
int main(int argc, char* argv[])
{
Caine grivei;
Pisica mitzi;
CFoo foo;
foo.SpuneCavaMaiAnimalule(&grivei);
foo.SpuneCavaMaiAnimalule(&mitzi);
return 0;
}
|
O prima incercare
Prima idee ar fi sa adaugãm un membru care sã þinã o informaþie despre tip.
| Citat: |
#include <iostream>
using namespace std;
class Animal
{
public:
enum TipAnimal {TipCaine, TipPisica};
};
class Caine : public Animal
{
public:
Caine() : m_tipAnimal(TipCaine) {}
const TipAnimal m_tipAnimal;
};
class Pisica : public Animal
{
public:
Pisica() : m_tipAnimal(TipPisica) {}
const TipAnimal m_tipAnimal;
};
class CFoo
{
public:
void SpuneCevaMaiAnimalule(Animal*);
};
int main(int argc, char* argv[])
{
// ... (vezi codul din enuntul problemei)
}
void CFoo::SpuneCevaMaiAnimalule(Animal* pAnimal)
{
if(((Caine*)pAnimal)->m_tipAnimal == Animal::TipCaine)
cout << "Ham! ";
else if(((Pisica*)pAnimal)->m_tipAnimal == Animal::TipPisica)
cout << "Miau! ";
}
|
Priviti, vã rog, la implementarea lui CFoo::SpuneCevaMaiAnimalule ºi gândiþi-vã cã am avea nu douã ci treizeci si nouã de tipuri (clase derivate din Animal).
Urâta rãu! Greu de scris, greu de inþeles, uºor de fãcut greseli. Deºi se putea si mai rãu...
...se poate si mai bine
Hai sa înbunãtãþim puþin soluþia!
Þin numele clasei într-un penis static privat de tip string ºi îl accesez printr-o funcþie virtualã.
| Citat: |
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
virtual bool EsteDeTipul(const string&) = 0;
};
class Caine : public Animal
{
static const string m_nume_clasa;
public:
virtual bool EsteDeTipul(const string&);
};
class Pisica : public Animal
{
static const string m_nume_clasa;
public:
virtual bool EsteDeTipul(const string&);
};
class CFoo
{
public:
void SpuneCevaMaiAnimalule(Animal*);
};
int main(int argc, char* argv[])
{
// ...
}
const string Caine::m_nume_clasa = "Caine";
bool Caine::EsteDeTipul(const string& nume_clasa)
{
return (nume_clasa == m_nume_clasa);
}
const string Pisica::m_nume_clasa = "Pisica";
bool Pisica::EsteDeTipul(const string& nume_clasa)
{
return (nume_clasa == m_nume_clasa);
}
void CFoo::DoSomething(Animal* pAnimal)
{
if(pAnimal->EsteDeTipul("Caine"))
cout << "Ham! " << endl;
else if(pAnimal->EsteDeTipul("Pisica"))
cout << "Miau! " << endl;
}
|
Din nou uitându-ne la CFoo::SpuneCevaMaiAnimalule, pare acum ceva mai clarã si mai uºor de scris.
Putem merge mai departe prin definirea urmatoarelor macro-uri:
| Citat: |
#define DECLARE_RUNTIME_CLASS
private:
static const string m_nume_clasa;
public:
virtual bool EsteDeTipul(const string&);
#define IMPLEMENT_RUNTIME_CLASS(class_name)
const string nume_clasa##::m_nume_clasa = #nume_clasa;
bool nume_clasa##::EsteDeTipul(const string& nume)
{return (nume == m_nume_clasa);}
|
Ne rãmane ºi mai puþin cod de scris:
| Citat: |
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
virtual bool EsteDeTipul(const string&) = 0;
};
class Caine : public Animal
{
DECLARE_RUNTIME_CLASS
};
class Pisica : public Animal
{
DECLARE_RUNTIME_CLASS
};
class CFoo
{
public:
void SpuneCevaMaiAnimalule(Animal*);
};
int main(int argc, char* argv[])
{
// ...
}
IMPLEMENT_RUNTIME_CLASS(Caine)
IMPLEMENT_RUNTIME_CLASS(Pisica)
void CFoo::SpuneCevaMaiAnimalule(Animal* pAnimal)
{
if(pAnimal->EsteDeTipul("Caine"))
cout << "Ham! ";
else if(pAnimal->EsteDeTipul("Pisica"))
cout << "Miau! ";
}
|
In felul acesta ne-am apropiat de soluþia din MFC (Microsoft Foundation Classes library).
Daca folosim MFC, atunci e floare la ureche
Majoritatea claselor din MFC deriva din CObject care printre altele implementeazã si un mecanism de verificare a tipului la run-time. Este un pic mai complicat decât în exemplul de mai sus dar tebuie sã-l iertam pentru ca face mai numeroase decât ne dea pur ºi simplu tipul obiectului. Mai multe despre "minunatele proprietati" ale clasei CObject pot face subiectul unui produs separat.
Oricum, dacã folosim MFC, problema noastrã se poate rezolva modest ºi elegant, cam asa
| Citat: |
#include <afx.h>
class Animal : public CObject
{
DECLARE_DYNAMIC(Animal)
};
class Caine : public Animal
{
DECLARE_DYNAMIC(Caine)
};
class Pisica : public Animal
{
DECLARE_DYNAMIC(Pisica)
};
class CFoo
{
public:
void SpuneCavaMaiAnimalule(Animal*);
};
int main(int argc, char* argv[])
{
// ...
}
IMPLEMENT_DYNAMIC(Animal, CObject)
IMPLEMENT_DYNAMIC(Caine, Animal)
IMPLEMENT_DYNAMIC(Pisica, Animal)
void CFoo::SpuneCavaMaiAnimalule(Animal* pAnimal)
{
if(pAnimal->IsKindOf(RUNTIME_CLASS(Caine)))
printf("Ham! ");
else if(pAnimal->IsKindOf(RUNTIME_CLASS(Pisica)))
printf("Miau! ");
}
|
O alta abordare: RTTI
O altã abordare este utilizarea mecanismul RTTI (Run-Time Type Information) al limbajului C++.
Acesta permite folosirea a doi operatori "speciali": typeid si dynamic_cast.
Prima rezolvare cu RTTI a problemei noastre este prin folosirea operatorului typeid. Acesta intoarce o referinþã la un type_info care þine informaþii despre tipul obiectului pasat ca parametru.
| Citat: |
#include <iostream>
using namespace std;
class Animal {virtual void Dummy(){};};
class Caine : public Animal{};
class Pisica : public Animal{};
class CFoo
{
public:
void SpuneCavaMaiAnimalule(Animal*);
};
int main(int argc, char* argv[])
{
// ...
}
void CFoo::SpuneCavaMaiAnimalule(Animal* pAnimal)
{
const type_info& ti = typeid(*pAnimal);
if(ti == typeid(Caine))
cout << "Ham! ";
else if(ti == typeid(Pisica))
cout << "Miau! ";
}
|
Frumos!
Poate aþi observat cã în clasa Animal am pus, deºi nu-mi trebuie la nimic, o funcþie virtualã. De ce? Simplu: ca sa meargã jucãrica cu RTTI-ul, trebuie o clasã de tip "polimorfic", adicã sa aibã cel puþin o funcþie virtualã.
Putem de întocmai sã ne bazãm pe operatorul dynamic_cast. Acesta întoarce zero daca îi plasãm un pointer la un obiect de tipul care nu trebuie. Deci am putea scrie
| Citat: |
void CFoo::SpuneCavaMaiAnimalule(Animal* pAnimal)
{
if(dynamic_cast<Caine*>(pAnimal))
cout << "Ham! ";
else if(dynamic_cast<Pisica*>(pAnimal))
cout << "Miau! ";
}
|
La urma urmei la ce bun?
Acestea fiind zise, am ºi eu o intrebare: chiar e nevoie sã ºtim tipul unui obiect la run-time? Eu zic ca nu. Putem uita toate chestiile de mai sus cu condiþia sã ne amintim de polimorfism si sã inþelegem pentru ce, de fapt, a bãgat nenea Stroustrup funcþiile virtuale în C++.
| Citat: |
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void IacaSpun() = 0;
};
class Caine : public Animal
{
public:
virtual void IacaSpun();
};
class Pisica : public Animal
{
public:
virtual void IacaSpun();
};
class CFoo
{
public:
void SpuneCavaMaiAnimalule(Animal*)
};
int main(int argc, char* argv[])
{
// ...
}
void Caine::IacaSpun()
{
cout << "Ham! ";
}
void Pisica::IacaSpun()
{
cout << "Miau! ";
}
void CFoo::SpuneCavaMaiAnimalule(Animal* pAnimal)
{
pAnimal->IacaSpun();
}
|
O ultimã privire la CFoo::SpuneCavaMaiAnimalule. Frumoasã, curatã, simplã, nu-i aºa?
The neverending story
Mi-am propus in articolul de faþã doar o introducere în "recunoaºterea tipurilor la run-time" prin rezolvarea unei probleme simple. Subiectul rãmâne deschis ºi se poate discuta în continuare pe aceasta temã.
Inchei, tot cu cu o micã problemã:
Completaþi codul de mai jos (aceeiaºi problemã, numai cã facem dynamic_cast la o referinþã ºi nu la un pointer).
| Citat: |
void CFoo::SpuneCavaMaiAnimalule(Animal& rAnimal)
{
dynamic_cast<Caine&>(rAnimal);
dynamic_cast<Pisica&>(rAnimal);
}
|
|
|
|
|
|
|
|
Fa-ti si tu propriul forum GRATUIT, pe www.myforum.ro
|
|