Conversia de tipuri
Un obiect al unei clase derivate este, în același timp, și un obiect al clasei de bază. Prin urmare, conversiile din tipul derivat în cel de bază se efectuează automat.
#include <iostream>
class Person
{
public:
Person(std::string name): name{name}
{ }
void print() const
{
std::cout << "Person " << name << std::endl;
}
private:
std::string name;
};
class Employee: public Person
{
public:
Employee(std::string name): Person{name} {}
};
int main()
{
Employee employee{"Bob"};
employee.print(); // Person: Bob
// conversie la tipul de bază
Person person1{employee}; // prin constructor de copiere
person1.print(); // Person: Bob
Person person2{"Tom"};
person2 = employee; // prin operator de atribuire
person2.print(); // Person: Bob
}
Aici, clasa Person este clasa de bază, iar Employee este derivată. Prin urmare, compilatorul poate converti automat un obiect Employee în tipul Person. Acest lucru se poate face cu ajutorul constructorului de copiere:
Person person1{employee};
Sau prin operatorul de atribuire:
Person person2{"Tom"};
person2 = employee;
Se pot efectua conversii și în mod explicit, folosind funcția static_cast():
int main()
{
Employee employee{"Bob"};
employee.print(); // Person: Bob
// conversie la tipul de bază
Person person1{static_cast<Person>(employee)}; // prin constructor de copiere
person1.print(); // Person: Bob
Person person2{"Tom"};
person2 = static_cast<Person>(employee); // prin operator de atribuire
person2.print(); // Person: Bob
}
Conversia pointerilor
Un pointer la un obiect al clasei derivate poate fi convertit automat într-un pointer la tipul de bază:
#include <iostream>
class Person
{
public:
Person(std::string name): name{name} { }
virtual void print() const
{
std::cout << name << std::endl;
}
std::string getName() const { return name; }
private:
std::string name;
};
class Employee: public Person
{
public:
Employee(std::string name, std::string company): Person{name}, company{company} {}
void print() const override
{
std::cout << getName() << " (" << company << ")" << std::endl;
}
std::string getCompany() const { return company; }
private:
std::string company;
};
int main()
{
Employee bob{"Bob", "Google"};
// conversie la pointer la clasa de bază
Person* person{&bob};
person->print(); // Bob (Google)
}
În acest caz, pointerul de tip Person primește adresa unui obiect de tip Employee.
La fel, putem avea un pointer de tip Employee, care este convertit automat în pointer la Person:
int main()
{
Employee bob{"Bob", "Google"};
Employee* employee = &bob;
// conversie la pointer la clasa de bază
Person* person{employee};
person->print(); // Bob (Google)
}
La fel se aplică și referințelor:
int main()
{
Employee sam{"Sam", "Microsoft"};
// referință de tip bază referă un obiect derivat
Person& person1 {sam};
person1.print(); // Sam (Microsoft)
Employee& employee{sam};
// conversie din referință derivată în referință la bază
Person& person2 {employee};
person2.print(); // Sam (Microsoft)
}
În unele cazuri este posibilă conversia inversă – de la clasa de bază la cea derivată. Însă, în primul rând, ea nu se realizează automat, pentru aceasta trebuie utilizate funcții de conversie, în special static_cast(). În al doilea rând, funcționarea ei depinde de tipul obiectului.
Pentru ca un obiect al clasei de bază, de exemplu Person, să poată fi convertit într-un pointer la clasa derivată, de exemplu Employee, pointerul clasei de bază trebuie să indice spre un obiect al clasei Employee (sau al unei clase derivate din Employee). Dacă nu este așa, rezultatul conversiei este nedefinit. De exemplu:
int main()
{
Employee sam{"Sam", "Microsoft"};
// pointerul clasei de bază indică spre un obiect al clasei derivate
Person* person {&sam};
// conversie inversă – din tipul de bază în cel derivat
Employee* employee{static_cast<Employee*>(person)};
employee->print(); // Sam (Microsoft)
}
Aici pointerul person, deși este de tip Person, în realitate indică spre un obiect Employee. Prin urmare, cu ajutorul funcției static_cast() acest pointer poate fi convertit în Employee*.
Dar să luăm o altă situație:
int main()
{
Person tom{"Tom"};
Person* person {&tom};
// conversie inversă – din tipul de bază în cel derivat
Employee* employee{static_cast<Employee*>(person)};
employee->print(); // Sam (Microsoft)
std::cout << employee->getCompany() << std::endl; // ???
}
Aici pointerul person indică spre un obiect Person. Totuși, cu ajutorul funcției static_cast, putem să-l convertim cu succes într-un pointer la Employee. Teoretic, printr-un astfel de pointer putem apela funcția getCompany, care este definită în clasa Employee. Dar în clasa Person această funcție nu există, iar la încercarea de a o apela, programul se va termina cu o eroare. De aceea, dacă nu există certitudinea că obiectul reprezintă o anumită clasă derivată, este mai bine să nu se efectueze astfel de conversii din tipul de bază în cel derivat.
Conversia smart-pointerilor
Smart-pointerii la clasa de bază pot de asemenea indica un obiect al clasei derivate:
int main()
{
std::unique_ptr<Person> bob{std::make_unique<Employee>("Bob", "Google")};
bob->print(); // Bob (Google)
std::shared_ptr<Person> tom{std::make_shared<Employee>("Tom", "Microsoft")};
tom->print(); // Tom (Microsoft)
std::shared_ptr<Employee> sam{std::make_shared<Employee>("Sam", "Jetbrains")};
std::shared_ptr<Person> person{sam};
person->print(); // Sam (Jetbrains)
}