前段時間在睿思上@vivianliu出了如下一道c++題目:
給定乙個類:
class mycl
protected:
virtual int geta()
};int main()
可見輸出是乙個未初始化的a。
先上**吧
class myder:mycl //同樣是派生乙個子類
};int main()
; //為下文修改記憶體分配策略
mycl* pacl = reinterpret_cast(data); //基於位的強制轉換
myder ader(*pacl);
ader.geta();
system("pause");
return 0;
}
從以上**可以看出,還有第二種方法來繞開預設建構函式的自動呼叫。
注意到mycl類的大小是
8個位元組
,前四個位元組是乙個指標,指向
虛函式表的位址
,後四個位元組則是類成員
a的大小了
。所以可以定義乙個大小為8個位元組的int型陣列:
int data[2] = ;
再利用強制轉換:
mycl* pacl = reinterpret_cast(data);
則可以構造乙個物件,相應的,後四個位元組的值為1,a則被初始化為1(怎麼感覺hack進了類的記憶體,所以這種強制轉化要慎用)。
但是這樣強制轉化之後,類虛函式表指標指向的值為0,也就是說並沒有初始化虛函式表,所以還需要一種手段來初始化虛函式表。弄乙個子類然後呼叫複製建構函式,就能實現虛函式表的自動初始化。(注意到子類複製建構函式中有一行注釋掉的geta(),把注釋去掉的話,直接呼叫子類複製建構函式便能呼叫基類的geta(),當然像現在這樣,宣告了
using:mycl::geta()
,直接在子類外頭呼叫也行)
這種方法不用構建乙個子類。可直接hack進類的虛函式表,通過函式指標來呼叫虛函式,即使該虛函式是private也管用。**:
int main()
(int*)ba,獲得虛函式表位址;
*(int*)ba,解引,獲得虛函式表;
(int*)*(int*)ba,獲得第乙個虛函式位址;
*((int*)*(int*)ba),獲得第乙個虛函式;
但是這種方法還有一種缺陷,就是函式指標pf並不知道ba的this指標的位址,所以通過pf呼叫的函式,a的值不是正確的,可能是任意記憶體位置的值。方法是改變函式指標的型別定義以及虛函式位址的傳遞方式。
int main()
可以看出pf定義為
(mycl::*func)(void)
型別,即可以視為mycl的乙個成員函式,通過
(*(func*)(*(int*)ba));的方法(獲取虛函式表後,通過
*(func*)
的方法獲取第乙個虛成員函式),複製給pf。再通過
(ba->*pf)();
的方式呼叫,可以認為中間的->就把this指標傳給了pf函式。這樣就能獲得正確的a的值,大家可以拿int data[2] = ;來初始化ba,再通過hack虛函式表的方式獲取a的值,就能發現這種方法是正確的。
陳皓在這篇文章也說了,c++是有缺陷的,c++也有其危險性,但是了解它的這種危險性能讓我們更好的學習這門語言,以下這篇文章也在記憶體級別分析了c++類的繼承原理:
謝謝大家!!!
由一道acm題目所想到
已經很長一段時間沒有ac題了,一周之前報名參加了計算客的程式設計大賽,雖然我的結果不 是特別理想,但是還是學到了一些東西。下邊,我先貼出題目和對應的程式原始碼 如下 c語言 includeint main if n 2 1 else if p n 1 p n 2 else totaltime p n...
由一道試題想到的
前段時間忙著換工作,面試了幾家公司,其中有些筆試題,蠻有意思的,給我很大啟發。最好玩的一道程式設計題 不使用中間變數,交換2個int型變數a和b的值。寫出方法。這個其實不是很難,方法如下 public class exchange void swap int a int b 給我的啟發是,在程式設計...
由SDRAM是一道坎想到的
自己頹廢了乙個星期,始終不想繼續,似乎快放棄了,心態開始轉變。也許是因為ta,也可能是自己沒勇氣面對sdram這道坎,趁著現在還清醒,站起來,別讓我看不起你 今天出去接人,看似簡單的事,但暴露出自己的很多問題 1.感覺和物流部經理溝通不夠自然,上次那件事之後,感覺很尷尬,自己就不能改改?2.在等修模...