近幾天看《ATL INTERNALS》,看到了附錄中的一個關(guān)于template的小技巧-仿真動態(tài)綁定:
template
class Array {
public:
……
virtual int Compare(const Array& rhs) =0;
bool operator< (const Array& rhs)
{ return this->Compare(rhs) < 0; }
bool operator> (const Array& rhs)
{ return this->Compare(rhs) >0; }
bool operator== (const Array& rhs)
{ return this->Compare(rhs) == 0; }
T m_rg[1024];
};
然后派生類重寫這個虛函數(shù)獲得多態(tài)特性:
class String : public Array {
public:
int Compare(const Array& rhs)
{ return strcmp(m_rg, rhs.m_rg); }
};
為了實現(xiàn)虛函數(shù)需要一個vptr和一個vtable,以及需要至少經(jīng)過兩次提領(lǐng)操作才能調(diào)用正確的函數(shù)。
然后書中介紹了一種提供效率的辦法:
template <typename T, typename Deriving>
class Array {
public:
……
bool operator< (const Array& rhs)
{ return static_cast(this)->Compare(rhs) < 0; }
bool operator> (const Array& rhs)
{ return static_cast(this)->Compare(rhs) > 0; }
bool operator== (const Array& rhs)
{ return static_cast(this)->Compare(rhs) == 0; }
T m_rg[1024];
};
注意Array模板接受一個附加的參數(shù)——派生類的名字。它利用這個類名完成堆自己的靜態(tài)強制轉(zhuǎn)換。因為編譯器會在實例化(具象化)派生類的同時展開基類的代碼,所以靜態(tài)強制轉(zhuǎn)換完成了一個完全安全的向下轉(zhuǎn)換。
class String : public Array<char, String> {
public:
int Compare(const Array& rhs)
{ return strcmp(m_rg, rhs.m_rg); }
};
這項技術(shù)在不使用虛成員的情況下使得我們看到并且感受到了動態(tài)綁定。真的有 那么神奇嗎?虛函數(shù)真的可用通過模板來模擬?當(dāng)然不是
一開始的時候我還覺得迷惑,但是隱約覺得這個東西只是看上去像虛函數(shù),但是限制多多。于是上網(wǎng)去問高手。gigix一開始被我說暈了,也怪我表述不好。問babysloth,一列出代碼他就知道了這個是什么了。他說: “這叫curiously recurring template pattern,由bell-lab的james coplien首先記錄下來”
緊接著,我偶然看到以前下載的ATL相關(guān)技術(shù)解析的文章,里面詳細(xì)說了這個東西。文章是Codeguru上的《ATL Under the Hood Part 3》。簡短的一個程序說明了這個東西:
#include
using namespace std;
class Base {
public:
virtual void fun() {
cout << "Base::fun" << endl;
}
void doSomething() {
fun();
}
};
class Drive : public Base {
public:
void fun() {
cout << "Drive::fun" << endl;
}
};
int main() {
Drive obj;
obj.doSomething();
return 0;
}
這段程序中的行為和下面的這段程序是一樣的:
#include
using namespace std;
template
class Base {
public:
void fun() {
cout << "Base::fun" << endl;
}
void doSomething() {
T* pT = static_cast(this);
pT->fun();
}
};
class Drive : public Base {
public:
void fun() {
cout << "Drive::fun" << endl;
}
};
int main() {
Drive obj;
obj.doSomething();
return 0;
}
這里說的結(jié)果是一樣的。引用蟲蟲的一句話:“效率不同”。
問題就是這里只是表現(xiàn)了虛函數(shù)的一個部分,虛函數(shù)一個經(jīng)典的例子是用數(shù)組來保存派生類指針,通過指針調(diào)用被改寫了的虛函數(shù)來表現(xiàn)多態(tài)。用這個模擬的版本可以這么作嗎?不行!
下面這么作是不行的:
#include
using namespace std;
template
class Base {
public:
void fun() {
cout << "Base::fun" << endl;
}
void doSomething() {
T* pT = static_cast(this);
pT->fun();
}
};
class Drive1 : public Base {
public:
void fun() {
cout << "Drive1::fun" << endl;
}
};
class Drive2 : public Base {
public:
void fun() {
cout << "Drive2::fun" << endl;
}
};
int main() {
Base* pBase = NULL;
pBase = new Drive1;
pBase->doSomething();
delete pBase;
pBase = new Drive2;
pBase->doSomething();
return 0;
}
因為Drive2與Drive1不匹配。另外這么模擬虛函數(shù)只能維持一層,不能像真正的虛函數(shù)一樣在派生的層次中任意的往下,虛函數(shù)一直都是虛函數(shù)。例如:
#include
using namespace std;
template
class Base {
public:
void fun() {
cout << "Base::fun" << endl;
}
void doSomething() {
T* pT = static_cast(this);
pT->fun();
}
};
class Drive : public Base {
public:
void fun() {
cout << "Drive::fun" << endl;
}
};
class MostDrive : public Drive {
public:
void fun() {
cout << "MostDrive::fun" << endl;
}
};
int main() {
MostDrive obj;
obj.doSomething();
return 0;
}
輸出是Drive::fun而不是MostDrive::fun
結(jié)論
結(jié)論就是這里并不是什么模擬虛函數(shù),只是在這個情景下很像虛函數(shù),虛函數(shù)的幾個重要的性質(zhì)并沒有支持。這個只不過是一個程序設(shè)計中的idiom,關(guān)于模板的諸多技巧中的一個。
主要用途:父類需要子類信息的時候。在父類中的一個函數(shù)a中需要調(diào)用子類中的某個函數(shù)b,以實現(xiàn)子類定制某些行為。
主要原理:通過對this的強制類型轉(zhuǎn)換實現(xiàn)對pT->fun()的不同解釋。這種解釋是在編譯期間的。編譯期間展開模板的類型參數(shù),根據(jù)參數(shù)確定了this的轉(zhuǎn)換到的類型,從而也確定了pT->fun()的解釋。所以模擬的是靜態(tài)的多態(tài)性。
使用方法:使用的方法不是像虛函數(shù)一樣通過對象的指針調(diào)用虛函數(shù)實現(xiàn)多態(tài)。而是通過調(diào)用從基類繼承來的某個普通函數(shù),在該函數(shù)中再去調(diào)用調(diào)用“經(jīng)過改寫”的派生類的函數(shù)。
主要缺陷:就是虛擬性質(zhì)不具有傳遞性,不具有動態(tài)的多態(tài)性。所謂的多態(tài)也只不過是通過你傳遞的模板參數(shù)所隱含的意思決定具體的函數(shù)調(diào)用。
感想:按照babysloth說法,這個也是一個設(shè)計模式。加上前段時間看《STL源碼剖析》感受的iterator等模式,覺得設(shè)計模式真是博大精深。等期中考試搞定了數(shù)學(xué)分析之后一定要向gigix借幾本這方面的書看看。
template
class Array {
public:
……
virtual int Compare(const Array& rhs) =0;
bool operator< (const Array& rhs)
{ return this->Compare(rhs) < 0; }
bool operator> (const Array& rhs)
{ return this->Compare(rhs) >0; }
bool operator== (const Array& rhs)
{ return this->Compare(rhs) == 0; }
T m_rg[1024];
};
然后派生類重寫這個虛函數(shù)獲得多態(tài)特性:
class String : public Array {
public:
int Compare(const Array& rhs)
{ return strcmp(m_rg, rhs.m_rg); }
};
為了實現(xiàn)虛函數(shù)需要一個vptr和一個vtable,以及需要至少經(jīng)過兩次提領(lǐng)操作才能調(diào)用正確的函數(shù)。
然后書中介紹了一種提供效率的辦法:
template <typename T, typename Deriving>
class Array {
public:
……
bool operator< (const Array& rhs)
{ return static_cast(this)->Compare(rhs) < 0; }
bool operator> (const Array& rhs)
{ return static_cast(this)->Compare(rhs) > 0; }
bool operator== (const Array& rhs)
{ return static_cast(this)->Compare(rhs) == 0; }
T m_rg[1024];
};
注意Array模板接受一個附加的參數(shù)——派生類的名字。它利用這個類名完成堆自己的靜態(tài)強制轉(zhuǎn)換。因為編譯器會在實例化(具象化)派生類的同時展開基類的代碼,所以靜態(tài)強制轉(zhuǎn)換完成了一個完全安全的向下轉(zhuǎn)換。
class String : public Array<char, String> {
public:
int Compare(const Array& rhs)
{ return strcmp(m_rg, rhs.m_rg); }
};
這項技術(shù)在不使用虛成員的情況下使得我們看到并且感受到了動態(tài)綁定。真的有 那么神奇嗎?虛函數(shù)真的可用通過模板來模擬?當(dāng)然不是
一開始的時候我還覺得迷惑,但是隱約覺得這個東西只是看上去像虛函數(shù),但是限制多多。于是上網(wǎng)去問高手。gigix一開始被我說暈了,也怪我表述不好。問babysloth,一列出代碼他就知道了這個是什么了。他說: “這叫curiously recurring template pattern,由bell-lab的james coplien首先記錄下來”
緊接著,我偶然看到以前下載的ATL相關(guān)技術(shù)解析的文章,里面詳細(xì)說了這個東西。文章是Codeguru上的《ATL Under the Hood Part 3》。簡短的一個程序說明了這個東西:
#include
using namespace std;
class Base {
public:
virtual void fun() {
cout << "Base::fun" << endl;
}
void doSomething() {
fun();
}
};
class Drive : public Base {
public:
void fun() {
cout << "Drive::fun" << endl;
}
};
int main() {
Drive obj;
obj.doSomething();
return 0;
}
這段程序中的行為和下面的這段程序是一樣的:
#include
using namespace std;
template
class Base {
public:
void fun() {
cout << "Base::fun" << endl;
}
void doSomething() {
T* pT = static_cast(this);
pT->fun();
}
};
class Drive : public Base {
public:
void fun() {
cout << "Drive::fun" << endl;
}
};
int main() {
Drive obj;
obj.doSomething();
return 0;
}
這里說的結(jié)果是一樣的。引用蟲蟲的一句話:“效率不同”。
問題就是這里只是表現(xiàn)了虛函數(shù)的一個部分,虛函數(shù)一個經(jīng)典的例子是用數(shù)組來保存派生類指針,通過指針調(diào)用被改寫了的虛函數(shù)來表現(xiàn)多態(tài)。用這個模擬的版本可以這么作嗎?不行!
下面這么作是不行的:
#include
using namespace std;
template
class Base {
public:
void fun() {
cout << "Base::fun" << endl;
}
void doSomething() {
T* pT = static_cast(this);
pT->fun();
}
};
class Drive1 : public Base {
public:
void fun() {
cout << "Drive1::fun" << endl;
}
};
class Drive2 : public Base {
public:
void fun() {
cout << "Drive2::fun" << endl;
}
};
int main() {
Base* pBase = NULL;
pBase = new Drive1;
pBase->doSomething();
delete pBase;
pBase = new Drive2;
pBase->doSomething();
return 0;
}
因為Drive2與Drive1不匹配。另外這么模擬虛函數(shù)只能維持一層,不能像真正的虛函數(shù)一樣在派生的層次中任意的往下,虛函數(shù)一直都是虛函數(shù)。例如:
#include
using namespace std;
template
class Base {
public:
void fun() {
cout << "Base::fun" << endl;
}
void doSomething() {
T* pT = static_cast(this);
pT->fun();
}
};
class Drive : public Base {
public:
void fun() {
cout << "Drive::fun" << endl;
}
};
class MostDrive : public Drive {
public:
void fun() {
cout << "MostDrive::fun" << endl;
}
};
int main() {
MostDrive obj;
obj.doSomething();
return 0;
}
輸出是Drive::fun而不是MostDrive::fun
結(jié)論
結(jié)論就是這里并不是什么模擬虛函數(shù),只是在這個情景下很像虛函數(shù),虛函數(shù)的幾個重要的性質(zhì)并沒有支持。這個只不過是一個程序設(shè)計中的idiom,關(guān)于模板的諸多技巧中的一個。
主要用途:父類需要子類信息的時候。在父類中的一個函數(shù)a中需要調(diào)用子類中的某個函數(shù)b,以實現(xiàn)子類定制某些行為。
主要原理:通過對this的強制類型轉(zhuǎn)換實現(xiàn)對pT->fun()的不同解釋。這種解釋是在編譯期間的。編譯期間展開模板的類型參數(shù),根據(jù)參數(shù)確定了this的轉(zhuǎn)換到的類型,從而也確定了pT->fun()的解釋。所以模擬的是靜態(tài)的多態(tài)性。
使用方法:使用的方法不是像虛函數(shù)一樣通過對象的指針調(diào)用虛函數(shù)實現(xiàn)多態(tài)。而是通過調(diào)用從基類繼承來的某個普通函數(shù),在該函數(shù)中再去調(diào)用調(diào)用“經(jīng)過改寫”的派生類的函數(shù)。
主要缺陷:就是虛擬性質(zhì)不具有傳遞性,不具有動態(tài)的多態(tài)性。所謂的多態(tài)也只不過是通過你傳遞的模板參數(shù)所隱含的意思決定具體的函數(shù)調(diào)用。
感想:按照babysloth說法,這個也是一個設(shè)計模式。加上前段時間看《STL源碼剖析》感受的iterator等模式,覺得設(shè)計模式真是博大精深。等期中考試搞定了數(shù)學(xué)分析之后一定要向gigix借幾本這方面的書看看。
浙公網(wǎng)安備 33010602011771號