Преобразование типов

Последнее обновление: 16.03.2023

Объект производного класса одновременно является объектом базового класса. Поэтому преобразования из производного типа в базовый выполняются автоматически.

#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-указателей

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)
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850