2012-06-19 5 views
2

В коде ниже приведены два класса. Создается один объект типа 2, а затем он назначается указателю класса один.Назначение объекта одного типа другому типу

При вызове функции out вызывается функция out класса class.

#include<iostream> 
using namespace std; 

class one 
{ 
    public : 
     void out() 
     { 
      cout<<"one "; 
     } 
}; 

class two 
{ 
    public : 
     void out() 
     { 
      cout<<"two "; 
     } 
}; 

int main() 
{ 
    two dp[3]; 
    one *bp = (one *)dp; 
    for (int i=0; i<3;i++) 
    (bp++)->out(); 
}  

ВЫВОД

one one one 

Выход по мне должно быть два, а не один. Когда мы создали объект типа 2, расположение памяти этого объекта содержало адрес функции вне класса два, то почему при назначении из класса один вызывается?

EDIT - Кроме того, даже если мы изменим имя функции в классе два, выход не будет изменен.

+1

Ну, вы солгали компилятору. Почему вы должны ожидать чего-то разумного? –

+3

Это [неопределенное поведение] (http://en.wikipedia.org/wiki/Undefined_behavior), простой и простой. – ildjarn

+0

почему undefined? –

ответ

4

Это не редкость для новичков предположить, что все Функции члена C++ «принадлежат» объекту.
Как вы заметили, они этого не делают.

Концептуально - точная процедура является деталью реализации компилятора - ваши out функции-члены превращаются в «свободных» функций, которые выглядят как эти:

void one_out(one* this) { cout << "one"; } 
void two_out(two* this) { cout << "two"; } 

Для функций, не являющихся членами, это все, что нужно.

Когда компилятор видит

(bp++)->out(); 

он знает, что пар является указателем на one (он не знает, что ты соврал), и поэтому он называет

one_out(bp++); 

, потому что это что делают компиляторы.

-3

Поскольку ваш метод out() не объявлен virtual, он отправляется на статический тип объекта, а не на тип времени выполнения объекта.

Кроме того, между этими двумя классами нет отношения подтипа, поэтому неправильно назначать указатели таким образом.

+4

'virtual' тоже не будет надежным, так как между рассматриваемыми классами нет отношения наследования. –

+0

Да, отношения наследования отсутствуют. –

+0

Я отредактировал этот ответ, пожалуйста, просмотрите его. – Wug

1

Выход на вашей машине, возможно, был «один один», но он мог так же легко взорваться, ушел & достал вам мороженое или запустил ракеты. Ваш код вызывает Undefined Behavior.

class one 
/*...*/ 

class two 
/*...*/ 

Обратите внимание, что one и two совершенно не связанных между собой классов. Вы не получаете two от one, или наоборот. Они совершенно разные.

Из-за этого ...

two dp[3]; 
one *bp = (one *)dp; 
for (int i=0; i<3;i++) 
    (bp++)->out(); 

Этот код вызывает неопределенное поведение *.bp не указывает на объект типа one, он указывает на объект типа two. Вы не можете бросать указатели таким образом, учитывая приведенный выше код.

(* Примечание:.. Неопределенное поведение было, когда вы пытались вызвать метод, когда объект был фактически twoone Кастинг себя не вызывает неопределенное поведение)

Этот литая синтаксис вы используете, (one *)dp является C-стиль, который в этом случае сводится к эквиваленту reinterpret_cast<one*>(bp);. Было бы лучше использовать reinterpret_cast, если это то, что вы на самом деле собираетесь делать, если не по какой-либо другой причине, кроме как написать самодокументирующий код.

Если вы действительно хотите получить one* от two*, у вас есть два варианта.

  1. Создать иерархию наследования, так что вы можете бросить, не вызывая UB
  2. Создать оператор преобразования, так что вы можете построить one из two или наоборот.

В вашем случае, так как вы цикл над массивом one объектов и пытаетесь выполнить two метод через эти указатели, лучше всего, вероятно, # 1 выше.

class one 
{ 
    public : 
     virtual void out() 
     { 
      cout<<"one "; 
     } 
}; 

class two : public one 
{ 
    public : 
     void out() 
     { 
      cout<<"two "; 
     } 
}; 

Теперь ваша петля будет работать, и код испустит «два два два» вместо того, что вы видели на самом деле.

Смежные вопросы