2007年9月23日
哈哈,從M$ Visual C++ Team的
Andy Rich那里又偷學到一招:VC8的隱含編譯項
/d1reportSingleClassLayout和
/d1reportAllClassLayout 。看個復雜的例子吧(如下),現在假設我們想知道Derived類的對象布局,怎么辦? 在Project Properties->C++->Command Line->Additional Options里面加上
/d1reportSingleClassLayoutDerived吧!
class CommonBase
{
int co;
};
class Base1: virtual public CommonBase
{
public:
virtual void print1() {}
virtual void print2() {}
private:
int b1;
};
class Base2: virtual public CommonBase
{
public:
virtual void dump1() {}
virtual void dump2() {}
private:
int b2;
};
class Derived: public Base1, public Base2
{
public:
void print2() {}
void dump2() {}
private:
int d;
};
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
F5編譯之,你會驚奇地發現,Output里面有如下字樣:
1 class Derived size(32):
2 +---
3 | +--- (base class Base1)
4 0 | | {vfptr}
5 4 | | {vbptr}
6 8 | | b1
7 | +---
8 | +--- (base class Base2)
9 12 | | {vfptr}
10 16 | | {vbptr}
11 20 | | b2
12 | +---
13 24 | d
14 +---
15 +--- (virtual base CommonBase)
16 28 | co
17 +---
18
19 Derived::$vftable@Base1@:
20 0 | &Base1::print1
21 1 | &Derived::print2
22
23 Derived::$vftable@Base2@:
24 0 | &Base2::dump1
25 1 | &Derived::dump2
26
27 Derived::$vbtable@Base1@:
28 0 | -4
29 1 | 24 (Derivedd(Base1+4)CommonBase)
30
31 Derived::$vbtable@Base2@:
32 0 | -4
33 1 | 12 (Derivedd(Base2+4)CommonBase)
34
35 Derived::print2 this adjustor: 0
36 Derived::dump2 this adjustor: 12
看到了嗎? VC8居然輸出了Derived對象的完整布局! 我們終于可以不必兩眼一抹黑般的去peek/poke了....第1行表明,Derived對象總占用了32字節;其由三部分組成,分別是行3-行7、行 8-行12、行13、行28;其中前二者分別是基類Base1、Base2的布局,最后的行28為虛擬基類Common的布局。
以基類 Base1部分為例,可發現其由一個虛函數表指針vftable和虛基表指針vbtable構成,先看Base1部分的vftable所指向的虛表$ vftable@Base1(行19),不難發現,其中的表項2已經被Derived::print2給override了;再來看Base2部分的 vftable所指向的虛表$vftable@Base2(行23),可發現,同樣的,Base2::dump2被Derived::dump2給 override了。這不明擺著就是虛函數機制嘛,heh~
值得注意的是,這個例子同時說明,多繼承場合下,其實在單一對象中是存在多個 this指針的....行35-36給出了如何將Derived的this指針校正為其基類子對象this指針的偏移量,也就是說,根據行36,假設有個 Derived d,那么d.dump1()實際上應該理解成通過虛表$vftable@Base2對((Base2*)(((char*)&d)+12))- >dump1()的調用....即傳遞給所有Base2成員函數的this指針應該是(Base2*)((char*)(&d)+12),這里可能我寫得恐怖了點,意思到了就成....這不,普通繼承、多繼承、對象Slicing的語義都在這個布局里面了,看仔細了哈~
OK,多繼承看完了,繼續看虛擬基類是如何布局的。虛基Common在Derived的布局中,位于Derived本身數據成員之后的位置。Base1、 Base2中均保存了一個vbtable指針,其分別指向各自所使用的虛基表$vbtable@Base1和$vbtable@Base2,為什么要指向一個虛基表? 很簡單,因為Base1、Base2有可能會同時繼承多個不同的虛擬基類.....這充分體現了C++對象布局的復雜性....在每個虛基表中,保存了所繼承的虛擬基類部分相對于子類部分vbtable指針的偏移值,以Base2為例,我們知道Base2的vbtable在Derived中的偏移值為16 (行10),則根據$vbtable@Base2,虛基Common部分距離Base2 vbtable指針的偏移值為12,則有虛基Common在Derived中的總偏移值為16+12。與普通多繼承同理,我們在調用非虛擬的虛基成員函數時,必須將Derived的this指針調整為指向虛基部分的this指針,只有這樣才能成功地訪問虛基自身的數據成員和虛基的虛擬函數(通過虛基自己的 vftable,為簡單起見,上例中我就沒弄那么復雜了,大家可以自己玩玩,明白如何舉一反三即可)
Transferring the Password
There are various ways to get the passwords stored in KeePass into other windows. The first, and most simple method is copying them to the clipboard. For this, just double-click onto the specific field in the main password list. Example: if you want to copy the password of entry X, point onto the password field of the entry in the main view and double-click. The password is copied to the clipboard. If you enable the auto-clearing option, KeePass will clear the clipboard automatically after some seconds. This prevents you from forgetting to clear the clipboard yourself, leaving sensitive data in the clipboard.
The second method is drag-n-drop. As in method 1, point onto the field you want to use, click the left mouse button and hold it. Drag the data into other windows.
The third, and the most powerful method is auto-type. KeePass features a very mighty auto-type feature, which types user names, passwords, etc. into other windows for you. The default auto-typing sequence is: {USERNAME}{TAB}{PASSWORD}{TAB}{ENTER}. But this sequence is customizable, per entry (read the CHM documentation file that comes with KeePass for more about this). This makes the auto-type feature applicable to all windows and webforms you'll ever see. There are two submethods to perform an auto-type:
- Selecting an entry: Just select the entry that you want to get auto-typed, right-click onto it and click "Perform Auto-Type". KeePass will minimize itself, the window that had the focus before will come to the front. KeePass starts typing the data into this window.
- Global hot key: This is the most powerful of all methods. You leave KeePass running in the background. As soon as you're on a site that requires a login (the password of which you stored in KeePass before), just press a hot key (by default, Ctrl-Alt-A). KeePass immediately auto-types the data into the target window.
#include <boost/config.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
template<class T>
inline T & ToRef( T *pT)
{
return *pT;
}
#define TODEF(x) bind( ToRef<x>, _1)
template<class T>
inline void DeletePointer( T *pT)
{
delete pT;
}
#define ALL(v) (v).begin(), (v).end()
struct X
{
public:
int m_t;
X():m_t(11)
{
}
bool f(int a)
{
std::cout << m_t << std::endl;
return true;
}
};
int main()
{
using namespace boost;
using namespace std;
vector<X *> v;
for( int i=0; i<10; ++i)
v.push_back( new X);
for_each( ALL(v), bind(&X::f, TODEF(X), 4 ) );
for_each( ALL(v), bind( DeletePointer<X>, _1) );
return 0;
}