Объект производного класса одновременно является объектом базового класса. Поэтому преобразования из производного типа в базовый выполняются автоматически.
#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 // преобразуем в базовый тип 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> 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) }