2014-12-05 2 views
-3

Почему dynamic_cast<new>(old) изменить old?dynamic_cast изменяет оригинальный объект?

Пример кода:

#include <iostream> 
#include <string> 

class MyInterface { 
public: 
    virtual void say() = 0; 
    virtual ~MyInterface() { } 
}; 

class SubInterface : public MyInterface { 
public: 
    std::string test = "test"; 
    virtual void say() = 0; 
    virtual ~SubInterface() { } 
}; 

class Example : public SubInterface { 
public: 
    void say() { 
    std::cout << test << std::endl; 
    } 
}; 


int main() { 
    MyInterface* ex1 = new Example(); 
    SubInterface* ex2 = dynamic_cast<SubInterface*>(ex1); 
    if (ex2 != nullptr) { 
    std::cout << "call ex2->say(): "; 
    ex2->test = "something else"; 
    ex2->say(); 
    } 
    else { 
    std::cout << "error" << std::endl; 
    } 

    std::cout << "call ex1->say(): "; 
    ex1->say(); 

    std::cerr << "debug: ex1=" << ex1 << "; ex2=" << ex2 << std::endl; 

    return 0; 
} 

который выводит:

call ex2->say(): something else 
call ex1->say(): something else 
debug: ex1=0xf1e010; ex2=0xf1e010 

Так я ожидал ex2 будет отличаться от ex1 и, следовательно, ожидается, ex2->test = "something else"; не менять ex1. Наверное, это предполагаемое поведение, но почему? (Если они не отличаются друг от друга, почему бы даже потребовать присвоить что-либо по результатам dynamic_cast? (Можно было бы продолжать использовать ex1?))

Любая помощь приветствуется.

+0

Вы видели http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used? – matsjoyce

+4

Почему вы ожидаете, что они будут другими? Это два указателя на один и тот же объект *, только через * различные интерфейсы *. – cdhowie

+0

Кстати, стоит заметить, что даже если исходное значение указателя в 'ex1' не было равно, что в' ex2', они все равно указывают на тот же объект. Вниз или вверх-литье указателя объекта может привести к другому адресу памяти, в зависимости от того, как компилятор выводит vtables для каждого класса в иерархии. (Это особенно верно при множественном наследовании.) – cdhowie

ответ

4

Я предполагаю, что это предназначенное поведение, но почему?

Вы производите указатель на другой тип, не создавая новый объект. Оба указывают на тот же объект Example; один рассматривает его как MyInterface, другой как SubInterface. Таким образом, после изменения объекта через один указатель изменения в нем видны через другой, поскольку оба указывают на один и тот же объект.

Если они не отличаются друг от друга, почему бы даже не требовать присвоения результата dynamic_cast?

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

Можно продолжать использовать ex1?

№ Даже если, как и в вашем случае, оно имеет одинаковое значение, оно имеет неправильный тип. Объявлено, что он указывает на MyInterface, в котором нет участника test, поэтому вы не можете сказать ex1->test.

1

ex1 и ex2 указывают на один и тот же объект, но с разными типами. Другими словами, память одна и та же (тот же объект Example), но как вы управляете памятью, осуществляется через два разных интерфейса (MyInterface или SubInterface).

dynamic_cast - это время выполнения, которое может использоваться для безопасного управления данными через другой (связанный) интерфейс, чем позволял бы тип другой переменной. Для этого много пользы, хотя ваш пример, конечно, не нужен.

1

Что делает dynamic_cast, просто уточняет, что фактический тип заостренного объекта соответствует тому, который вы ожидаете, и возвращает nullptr, если они этого не делают. Если они совпадают, он возвращает указатель на тот же объект, как если бы вы сделали static_cast (образно говоря, есть некоторые другие тонкие отличия от проверки времени выполнения). Эта дополнительная проверка не создает новые объекты. dynamic_cast не изменил объект - вы сделали.

+1

Он может фактически возвращать другое значение указателя, но одно, которое указывает (и, следовательно, будет сравнивать его) с исходным объектом. – cdhowie

+0

@cdhowie Спасибо, прояснил это. – BartoszKP

+1

Спасибо. Для тех, кто смущен тем, что я имею в виду, [вот пример] (http://ideone.com/QW7U7p). Обратите внимание, что указатель 'B' не совпадает с указателем' C', но он по-прежнему сравнивается с ним. – cdhowie

1

Dynamic_cast никогда не изменяет оригинальный объект, в вашем случае «ex1». Первоначально выделенная память остается неизменной. В противном случае у вас будут все виды утечек и повреждение памяти.

Ваша цель также состоит в том, чтобы понять, может ли она дать вам указатель, отличный от входного.

Чтобы понять это, вы должны поэтому в двух направлениях - и приведения к базовому типу и понижающее приведение наследования и членов переменных и т.д.

Есть отличные ответы Другие по этому вопросу. Предложите искать похожие.