一道阿里实习生笔试题的疑惑?

问题:
#include <iostream> using namespace std; class animal { protected: int age; public: virtual void print_age(void) = 0; }; class dog : public animal { public: dog() {this -> age = 2;} ~dog() { } virtual void print_age(void) {cout<<"Wang, my age = "<<this -> age<<endl;} }; class cat: public animal { public: cat() {this -> age = 1;} ~cat() { } virtual void print_age(void) {cout<<"Miao, my age = "<<this -> age<<endl;} }; int main(void) { cat kitty; dog jd; animal * pa; int * p = (int *)(&kitty); int * q = (int *)(&jd); p[0] = q[0]; pa = &kitty; pa -> print_age(); return 0; }
输出是:
Wang, my age = 1

今天线上笔试遇到的一道题,很好奇,这几句:
int * p = (int *)(&kitty);
int * q = (int *)(&jd);
p[0] = q[0];

这是为什么呢?
--------------------------------------------------------------------------------
确实是:
p[0] = q[0];
我没有记错。

当时读题时,看到 基类,派生类,虚函数,,我就猜到肯定是要考 多态,虚函数表这些知识,
当时就是对这句挺疑惑:
p[0] = q[0];
蓝色 大大,这么晚还回答了

这个是昨天笔试完,晚上11点左右提的,不能算我笔试违规吧? T_T

好吧,我也不匿名了,感觉匿名不好,不懂就问,为啥要匿名。
我不是 陈浩大大,只是个普通院校 大三 计算机科班生。
 
解答:
作者:RednaxelaFX
链接:https://www.zhihu.com/question/29256578/answer/43725188
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

首先

@蓝色
大大的答案已经把思路说得很清楚了。这是个hack,不是C++语言规范所保证的行为,而是某些C++编译器采用的C++ ABI的行为。

 

放个传送门:为什么bs虚函数表的地址(int*)(&bs)与虚函数地址(int*)*(int*)(&bs) 不是同一个? - RednaxelaFX 的回答

其次,这代码不但依赖某些C++编译器的行为,还依赖平台的指针宽度是32位。
int * p = (int *)(&kitty);
int * q = (int *)(&jd);
p[0] = q[0];
这几句不应该用int*,而应该用intptr_t*才对。这样才能保证拷贝的是一个指针宽度的数据,而不是一个int宽度的数据。
  • 在32位平台上,int通常是32位,而指针是32位,所以正好匹配了,程序能正常运行;
  • 在64位平台上,如果是流行的LP64模型,int是32位而指针是64位,这里实际上只拷贝了指针的一半,程序能否正常运行就看运气了。

如果是在一个64位且小端(little endian)的平台上,那这代码拷贝的是指针的低32位。很可能会运气好能正常运行,因为dog类与cat类的vtable可能正好在内存里处于很近的位置,它们的地址的高32位可能正好相同,地址不同的地方都在低32位,这样这个程序就运气好能正常运行。
如果是在一个64位且大端(big endian)的平台上,那这段代码拷贝的是指针的高32位,那就完全达不到效果了。

不知道谁出的这种题⋯
或者题主把题目的细节记错了。
后面有回答说原本的笔试题不是p[0] = q[0],而是p[1] = q[1]。如果是这样的话那仍然只能在32位平台上能行,在64位平台上就纱布了。

再次,这种题还有很多玩法。例如说一种简单的玩法是像这样:
#include <iostream>
using namespace std;

class animal
{
protected:
  int age_;
  animal(int age): age_(age) { }

public:
  virtual void print_age(void) = 0;
  virtual void print_kind() = 0;
  virtual void print_status() = 0;
};

class dog : public animal
{
public:
  dog(): animal(2) { }
  ~dog() { }

  virtual void print_age(void) {
    cout << "Woof, my age = " << age_ << endl;
  }

  virtual void print_kind() {
    cout << "I'm a dog" << endl;
  }

  virtual void print_status() {
    cout << "I'm barking" << endl;
  }
};

class cat : public animal
{
public:
  cat(): animal(1) { }
  ~cat() { }

  virtual void print_age(void) {
    cout << "Meow, my age = " << age_ << endl;
  }

  virtual void print_kind() {
    cout << "I'm a cat" << endl;
  }

  virtual void print_status() {
    cout << "I'm sleeping" << endl;
  }
};

void print_random_message(void* something) {
  cout << "I'm crazy" << endl;
}

int main(void)
{
  cat kitty;
  dog puppy;
  animal* pa = &kitty;

  intptr_t* cat_vptr = *((intptr_t**)(&kitty));
  intptr_t* dog_vptr = *((intptr_t**)(&puppy));

  intptr_t fake_vtable[] = {
    dog_vptr[0

相关文章: