在下面的代码中有两个类。创建一个类型为2的对象,然后将其分配给第一类的指针。
在调用out函数时,调用第一类的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的对象时,该对象的内存位置包含了类2之外的函数的地址,那么为什么在赋值时,第一类被调用?
编辑 - 而且即使我们更改了第二类中的函数名称,输出也不会改变。
答案 0 :(得分:4)
新手假设所有 C ++成员函数&#34;属于&#34;并不少见。对象。
如你所知,他们没有。
从概念上讲 - 精确的过程是编译器实现细节 - 您的out
成员函数转换为&#34; free&#34;看起来像这样的函数:
void one_out(one* this) { cout << "one"; }
void two_out(two* this) { cout << "two"; }
对于非会员功能,这就是所有需要的。
当编译器看到
时 (bp++)->out();
它知道 bp是指向one
的指针(它不知道你撒谎),所以它调用
one_out(bp++);
因为那是编译器的作用。
答案 1 :(得分:1)
你的机器上的输出可能是“一一”,但它可能很容易被炸毁,消失了&amp;得到你的冰淇淋,或发射导弹。您的代码唤起了未定义的行为。
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
的对象。鉴于上面的代码,你不能以这种方式转换指针。
(*注意:未定义的行为是指当对象实际上是one
时尝试调用two
方法时。转换本身不会引起未定义的行为。)
您正在使用的演员语法,(one *)dp
是一个C风格的演员,在这个例子中归结为reinterpret_cast<one*>(bp);
的等效。如果除了编写自我记录代码之外没有其他原因,最好使用reinterpret_cast
这是你真正打算做的事情。
如果你真的想从one*
获得two*
,你有两种选择。
one
构建two
,反之亦然。在您的情况下,由于您循环遍历one
个对象并尝试通过这些指针执行two
方法,因此您最好的选择可能是#1以上。
class one
{
public :
virtual void out()
{
cout<<"one ";
}
};
class two : public one
{
public :
void out()
{
cout<<"two ";
}
};
现在你的循环将起作用,代码将发出“两个两个”,而不是你实际看到的任何随机行为。
答案 2 :(得分:-3)
由于您的out()
方法未声明virtual
,因此会在对象的静态类型上调度它,而不是对象的运行时类型。
此外,两个类之间没有子类型关系,因此以这种方式分配指针是不正确的。