2012-06-19 143 views
2

在代码下面有两个类。创建一个类型为2的对象,然后将其分配给类1的指针。将一种类型的对象分配给另一种类型

在调用out函数时,调用class 1的out函数。

#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的对象时,那个对象的内存位置包含了第二类以外的函数的地址,那么为什么在分配时,第一类被调用?

编辑 - 此外,即使我们改变了第二类功能的名称,输出没有改变。

+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

'虚拟'也不可靠,因为这些类之间没有继承关系。 –

+0

是的,没有继承关系。 –

+0

我编辑了这个答案,请检查它。 – Wug

1

您的机器上的输出可能是“一个一个”,但它可能很容易炸毁,&得到冰淇淋,或发射导弹。您的代码唤起未定义的行为。

class one 
/*...*/ 

class two 
/*...*/ 

注意onetwo是完全不相关的类。您不是从one得到two,反之亦然。它们是完全不同的类型。

正因为如此...

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

此代码唤起未定义行为*。bp不指向one类型的对象,它指向two类型的对象。鉴于上述代码,您不能以这种方式投射指针。

(*注意:未定义的行为,当你试图调用一个one方法时,对象竟是two铸件本身不引起未定义行为)

您正在使用该转换语法,(one *)dp是一个C型的演员,在这种情况下归结为reinterpret_cast<one*>(bp);的平等。如果没有其他原因,那么最好使用reinterpret_cast,这是你真正打算做的事情,而不是编写自我记录代码。

如果你真的想从two*得到one*,你有两种选择。

  1. 创建的传承层次,这样就可以不用唤起UB
  2. 创建一个转换操作符,这样就可以从two反之亦然构建一个one投。

在你的情况,因为你遍历one对象的数组,并试图通过执行这些指针two方法,最好的办法是上述可能#1。

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

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

现在你的循环将工作,代码将发出“两个两个二”,而不是你实际看到的任何随机行为。

相关问题