標籤:print 彙編 成員變數 main 普通人 int 問題 情況 public
做項目的過程中,碰到一個問題。
問題可以抽象為下面的問題:
普通人吃飯拿筷子,小孩吃飯拿勺子。
class People {public: void eat() { get_util_to_eat(); } virtual void get_util_to_eat() { std::cout << "People get chopsticks" << std::endl; }};class Children : public People {public: void get_util_to_eat() { std::cout << "Children get scoop" << std::endl; }};int main() { People* people = new Children(); people->eat(); return 0;}
輸出結果:
Children get scoop
當然這也符合我們的預期。
因為people不是虛函數,所以上述程式調用的是people中的eat方法,這就涉及到一個之前我一直模糊的概念,在一個類方法中調用虛方法,是如何調用的。
這又涉及到之前不得不說的一個問題:
class A {public: void print() { std::cout << "i am A" << std::endl; }};int main() { A* a = NULL; a->print(); return 0;}
上述代碼會輸出什麼,按照直觀的感覺NULL怎麼可能調用方法呢,要出core吧。
但是事實上,輸出的是:
i am A
調用類函數的時候,c++編譯器並不會管該類是否為空白,而是將該類的地址當做this指標傳到函數中去。
a->print() 時,在編譯器中就相當於print(&a)
有因為print中沒有用到成員變數的情況,所以自然能很正確的運行。
然後來看下彙編代碼就能更理解了。以下是People類內的彙編代碼。
21 void eat() { 0x0000000000400bd2 <+0>: push %rbp 0x0000000000400bd3 <+1>: mov %rsp,%rbp 0x0000000000400bd6 <+4>: sub $0x10,%rsp 0x0000000000400bda <+8>: mov %rdi,-0x8(%rbp) //rsp表示第一個參數,也就是類的指標
get_util_to_eat(); 0x0000000000400be9 <+23>: mov -0x8(%rbp),%rax //將類指標放入rax寄存器中 0x0000000000400bed <+27>: mov (%rax),%rax //取首地址值,也就是虛表地址 0x0000000000400bf0 <+30>: mov -0x8(%rbp),%rdi //放入rdi中,下次函數調用的時候取參用 0x0000000000400bf4 <+34>: mov (%rax),%rax //取出虛表中函數的地址 0x0000000000400bf7 <+37>: callq *%rax //調用改函數
總結就是,進入類的非靜態成員函數時,會預設攜帶類的指標(this),然後改函數內用到成員變數、成員方法都等同於在前面加了一個this->
So 回到最初的那個問題,在People::eat中傳入的是Chilren的指標,所以調用 get_util_to_eat 時從虛表中取出了Children::get_util_to_eat方法並進行調用。
c++ 類內建函式調用虛函數