Объект производного класса одновременно является объектом базового класса. Поэтому преобразования из производного типа в базовый выполняются автоматически.
#include <iostream>
#include <string>
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
// преобразуем в базовый тип
Person person1{employee}; // через конструктор копирования
person1.print(); // Person: Bob
Person person2{"Tom"};
person2 = employee; // через операцию присваивания
person2.print(); // Person: Bob
}
Здесь класс Person является базовым, а Employee производным. Поэтому компилятор может автоматически преобразовать объект Employee в тип Person. Это можно сделать с помощью конструктора копирования:
Person person1{employee};
Или через операцию присваивания:
Person person2{"Tom"};
person2 = employee;
Но также можно выполнять преобразования явным образом, например, с помощью функции static_cast():
int main()
{
Employee employee{"Bob"};
employee.print(); // Person: Bob
// преобразуем в базовый тип
Person person1{static_cast<Person>(employee)}; // через конструктор копирования
person1.print(); // Person: Bob
Person person2{"Tom"};
person2 = static_cast<Person>(employee); // через операцию присваивания
person2.print(); // Person: Bob
}
Указатель на объект производного класса можно преобразовать автоматически в указатель на объект базового типа:
#include <iostream>
#include <string>
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"};
// преобразуем в указатель на базовый тип
Person* person{&bob};
person->print(); // Bob (Google)
}
В данном случае указатель на объект Person получает адрес объекта Employee.
Подобным образом можно создать указатель производного класса и преобразовать автоматически в указатель на базовый тип
int main()
{
Employee bob{"Bob", "Google"};
Employee* employee = &bob;
// преобразуем в указатель на базовый тип
Person* person{employee};
person->print(); // Bob (Google)
}
То же самое касается ссылок:
int main()
{
Employee sam{"Sam", "Microsoft"};
// ссылка базового типа ссылается на объект производного класса
Person &person1 {sam};
person1.print(); // Sam (Microsoft)
Employee &employee{sam};
// преобразуем ссылку производного класса в ссылку базового класса
Person &person2 {employee};
person2.print(); // Sam (Microsoft)
}
В некоторых случаях возможно приведение в обратную сторону- от базового к производному. Но, во-первых, автоматически оно не выполняется, для этого надо
использовать функции преобразования, в частности, static_cast(). Во-вторых, будет оно работать или нет, зависит от типа объекта. Чтобы можно
было привести объект базового класса, например, Person, к указателю производного класса, например Employee, указатель базового класса должен указывать на объект
класса Employee (или классов, производных от Employee). Если это не так, то результат приведения не определен. Например:
int main()
{
Employee sam{"Sam", "Microsoft"};
// указатель базового класса указывает на объект производного класса
Person* person {&sam};
// обратное преобразование - из базового типа в производный
Employee* employee{static_cast<Employee*>(person)};
employee->print(); // Sam (Microsoft)
}
Здесь указатель person, хоть и представляет указатель на тип Person, в реальности указывает на объект Employee. Поэтому с помощью функции static_cast() этот указатель
можно привести к типу Employee*.
Но возьмем другую ситуацию:
int main()
{
Person tom{"Tom"};
Person* person {&tom};
// обратное преобразование - из базового типа в производный
Employee* employee{static_cast<Employee*>(person)};
employee->print(); // Sam (Microsoft)
std::cout << employee->getCompany() << std::endl; // ???
}
Здесь указатель person указывает на объект Person. Однако с помощью функции static_cast мы можем успешно его привести к указателю на Employee. Теоретически через подобный указатель мы можем обратиться к функции getCompany, которая определена в классе Employee. Но в классе Person ее нет, и поэтому при попытке к ней обратиться программа завершится с ошибкой. Поэтому если нет уверенности, что объект представляет определенный производный класс, то лучше не выполнять подобные преобразования из базового типа в производный.
smart-указатели на базовый класс также могут указывать на объект производного класса
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)
}