Daniro_San
@Daniro_San
Программист

Приведение обьекта производного класса к базовому?

Экспериментирую с ООП.
Если привести обьект производного класса к базовому, поля объявленные в производном классе исчезают? А если использовать приведение ссылок?
Например:
class Base{};
class Exp: public Base
{
int i=0;
public:
           int Get() const {  return i; }
           void Set(const int &num) { i=num; }
};
std::vector<Base> MyStack;
Base &GetRef()
{           return *MyStack.begin();               }
int main(){
                Exp a;
                a.Set(4);
                MyStack.push_back(a);
                int res=((Exp&)GetRef()).Get(); // Не работает, мусор из стека
}
  • Вопрос задан
  • 1241 просмотр
Решения вопроса 2
Nipheris
@Nipheris Куратор тега C++
поля объявленные в производном классе исчезают?

Это называется object slicing. Это явление проявляется в языках, где сложные типы данных вроде классов и записей а) могут наследоваться и добавлять новые поля при наследовании; б) могут присваиваться по-значению (by value). Это и приводит к тому, что поля потомка при присвоении в переменную типа предка отсекаются. Такое присвоение само по себе некорректно, т.к. если вы работаете с объектами классов-наследников через интерфейс базового класса, это подразумевает полиморфное поведение и работу через ссылку/указатель, т.е. чтобы вместо самого объекта копировалась ссылка/указатель на него (т.е. его identity, "уникальный ключ"), а сам оставался нетронутым и лежал там же, где и лежал.

При работе через ССЫЛКУ на базовый класс (т.е. Base&) таких проблем не будет, однако ссылка сама по себе переменной не является (в отличие от указателя) - это лишь дополнительное ИМЯ для некоторой другой переменной или области памяти. Поэтому, вы не сможете сохранить ссылку в векторе - ваш код не скомпилируется. В векторе вам нужно будет хранить указатели. Например, std::vector<Base*>.

Запомните простое правило - если ваши объекты подразумевают работу через интерфейс базового класса (т.е. полимофрное поведение), то в большинстве случаев вам следует работать с ними через указатель (и, в большинстве случаев, размещать эти объекты в куче). В том числе через умные указатели (unique_ptr, shared_ptr).

В других языках (C#, D вроде тоже) существует фундаментальное разделение на типы-значения и ссылочные типы, и для всех ссылочных типов работа "через ссылку" предоставляется автоматически. В C++ такого разделения нет, и как работать с типом вы выбираете сами, при его ИСПОЛЬЗОВАНИИ (т.е. используете либо по-значению, либо через указатель).

P.S. С днем рождения!
Ответ написан
@Mercury13
Программист на «си с крестами» и не только
Через указатели и ссылки — останется.
По значению (как у тебя сейчас в векторе) — преобразуется в базовый.

Главная проблема работы с указателями и ссылками — где хранить объекты и как их уничтожать. Хранят обычно в «куче», а для уничтожения используют умные указатели или что-то самописное под задачу. Вот, например, решение на умных указателях C++11:
#include <iostream>
#include <memory>
#include <vector>

class Base{
public:
    // Нужен виртуальный деструктор, ведь мы будем уничтожать детей
    // через родительский ук-ль. Да и dynamic_cast требует RTTI,
    // а значит, хоть одного виртуального.
    virtual ~Base() = default;
};

class Exp: public Base
{
int i=0;
public:
       int Get() const {  return i; }
       void Set(const int &num) { i=num; }
};

std::vector<std::shared_ptr<Base>> MyStack;
Base &GetRef() { return **MyStack.begin();  }

int main() {
    std::shared_ptr<Exp> a = std::make_shared<Exp>();
    a->Set(4);
    MyStack.push_back(a);
    int res=dynamic_cast<Exp&>(GetRef()).Get(); // Теперь работает
    std::cout << res << std::endl;;
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@LexArd
Найди эту книгу: www.ozon.ru/context/detail/id/1631049 там все это расписано, на сколько я помню.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы