腾讯模拟笔试题,为什么C++代码输出是daxbe

代码如下:
class x
{
public:
    virtual void f(){cout<<"x"<<endl;}
};
class a:public x
{
public:
    virtual void f(){cout<<"a"<<endl;}
};
class b:public x
{
public:
    virtual void f(){cout<<"b"<<endl;}
};
class d:public a
{
public:
    virtual void f(){cout<<"d"<<endl;}
};
class e:public a
{
public:
    virtual void f(){cout<<"e"<<endl;}
};
int main()
{
    vector<void*> v;
    /*1\. 对于自定义类类型:
 如果该类没有定义构造函数(由编译器合成默认构造函数)但有虚函数,
 那么class c = new class;和class c = new class();一样,都会调用默认构造函数。
    */
    v.push_back(new d);
    v.push_back(new a);
    v.push_back(new x);
    v.push_back(new b);
    v.push_back(new e);
    for(int i=0;i<v.size();i++)
    {
        static_cast<b*>(v[i])->f();
    }
    return 0;
}

继承关系大家可以从代码看到。 d/a/x/b/e的实例指针分别转化为void*指针,然后再转化成b*类型指针,为什么输出是:

d
a
x
b
e

为什么a/d/e和b并没有继承关系,转化后依然可以输出子类的内容?

#腾讯##C++工程师#
全部评论
我的理解是:因为每个类对象所占的空间都是虚函数指针的大小。因为没有继承关系,所有的虚函数指针都指向自己的f()函数。强制转换后,并不影响指针所指向的内容,输出都是读取指定偏移量中的虚函数指针指向的内容。
点赞
送花
回复
分享
发布于 2016-09-02 10:25
vector 里边的是 void* ,static_cast 里边的是B*。 转换为void* 再转换为B*的时候,只是这个指针的解释方式变了,指针的值不变,所以调用的还是各个类自身的虚函数。
点赞
送花
回复
分享
发布于 2016-09-02 11:05
滴滴
校招火热招聘中
官网直投
转化为b类型再调用fun也就是取出对象最开始4字节虚表的地址再去访问对应虚函数,由于覆盖,每个对象里自己的同名函数覆盖了子类,则只有一个自己的虚函数也就是调用自己的虚函数
点赞
送花
回复
分享
发布于 2016-09-02 11:06
为什么a/d/e和b并没有继承关系,转化后依然可以输出子类的内容? 这是因为静态绑定。 个人理解,大神们轻拍。 v.push_back(new d); v.push_back(new a); v.push_back(new x); v.push_back(new b); v.push_back(new e); v里面的5个指针,在编译的时候都已经知道所指对象的虚函数表中的函数实现(静态绑定)。 后续虽然有指针的类型转换,但是指针所指向的对象没有变,就不存在动态绑定。 vptr是保存在对象的内存空间中的,这点很重要。
点赞
送花
回复
分享
发布于 2016-09-02 11:10
先说一下static_cast 用static_cast 转换的时候是不安全的, 该代码能将不是继承关系的指针转为b , 不会出现编译错误的前提是 声明的时候, 其余几个指针类型都是void*。 虽然可以转换, 但是也只是能调用函数, 不能正常输出成员变量的值, 因为static_cast不安全, 它会 将错误保留下去, 大家可以试试。 #include <iostream> using namespace std;class x { public: int i; x(){i=1;} virtual void f(){cout<<"x"<<endl;} }; class a:public x { public: int j; a(){j=2;} void f(){cout<<"a"<<endl;} }; int main() { x *p1 = new x; a *p2 = static_cast<a*>(p1); p2->f();//x cout<<p2->i<<endl;//1 cout<<p2->j<<endl;//0 return 0; } //static_cast将一个指向x类型的指针转为指向a类型的指针, //虽然正确,但是不安全, 因为此时p2并不能访问j,它访问的是 //垃圾值. 如果使用dynamic_cast 他就会检查出这种不安全 //情况, 从而返回NULL. 另外,楼主的函数之所以会这样输出daxbe 我觉得是因为 每一个类都继承x, x中的函数是virtual类型的, 那派生类重写的函数也是virtual类型的, 既然是虚函数, 调用的时候就会去虚函数表中找函数地址,找虚函数表的时候不看指针类型,只看实际指向的内容(这就是能多态的一部分原因),因为他们实际指向各自的函数,所以最后会输出各自的结果。 至于楼上说的静态绑定,我觉得不对,静态绑定是指普通函数的调用使用的是静态绑定,一旦函数是虚函数,就不能是静态绑定的,一定是通过查表才决定绑定哪个函数。 我也是小白,如果大家有觉得我说的不对的地方,帮我指正啦。
点赞
送花
回复
分享
发布于 2016-09-02 20:34
我怎么记得昨天不是每一个f()前面都有virtual的??
点赞
送花
回复
分享
发布于 2016-09-02 10:41
最好有原题吧,有一些语法错误,没法做……
点赞
送花
回复
分享
发布于 2016-09-02 11:01
为什么我的模拟笔试题没有编程题…和岗位有关吧?
点赞
送花
回复
分享
发布于 2016-09-02 12:05
楼主的给的代码优点小问题,已修正,能跑了 class x { public: virtual void f(){ cout << "x" << endl; } }; class a :public x { public: virtual void f(){ cout << "a" << endl; } }; class b :public x { public: virtual void f(){ cout << "b" << endl; } }; class d :public a { public: virtual void f(){ cout << "d" << endl; } }; class e :public a { public: virtual void f(){ cout << "e" << endl; } }; int main() { vector<void*> v; /*1\. 对于自定义类类型: 如果该类没有定义构造函数(由编译器合成默认构造函数)但有虚函数, 那么class c = new class;和class c = new class();一样,都会调用默认构造函数。 */ v.push_back(new d); v.push_back(new a); v.push_back(new x); v.push_back(new b); v.push_back(new e); for (int i = 0; i<v.size(); i++) { static_cast<b*>(v[i])->f(); } return 0; }
点赞
送花
回复
分享
发布于 2016-09-04 22:00

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务