dw如何用表格做網(wǎng)站免費網(wǎng)頁制作平臺
條款 44:將與參數(shù)無關的代碼抽離模板
模板可以節(jié)省時間和避免代碼重復,編譯器會為填入的每個不同模板參數(shù)具現(xiàn)化出一份對應的代碼,但長此以外,可能會造成代碼膨脹(code bloat),生成浮夸的二進制目標碼。
基于共性和變性分析(commonality and variability analysis)?的方法,我們需要分析模板中重復使用的部分,將其抽離出模板,以減輕模板具現(xiàn)化帶來的代碼量。
- 因非類型模板參數(shù)而造成的代碼膨脹,往往可以消除,做法是以函數(shù)參數(shù)或類成員變量替換模板參數(shù)。
- 因類型模板參數(shù)而造成的代碼膨脹,往往可以降低,做法是讓帶有完全相同二進制表述的具現(xiàn)類型共享實現(xiàn)代碼。
參考以下矩陣類的例子:
template<typename T, std::size_t n>
class SquareMatrix {
public:void Invert();...
private:std::array<T, n * n> data;
};
修改為:
template<typename T>
class SquareMatrixBase {
protected:void Invert(std::size_t matrixSize);...
private:std::array<T, n * n> data;
};template<typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T> { // private 繼承實現(xiàn),見條款 39using SquareMatrixBase<T>::Invert; // 避免掩蓋基類函數(shù),見條款 33public:void Invert() { this->Invert(n); } // 調用模板基類函數(shù),見條款 43...
};
Invert
并不是我們唯一要使用的矩陣操作函數(shù),而且每次都往基類傳遞矩陣尺寸顯得太過繁瑣,我們可以考慮將數(shù)據(jù)放在派生類中,在基類中儲存指針和矩陣尺寸。修改代碼如下:
template<typename T>
class SquareMatrixBase {
protected:SquareMatrixBase(std::size_t n, T* pMem): size(n), pData(pMem) {}void SetDataPtr(T* ptr) { pData = ptr; }...
private:std::size_t size;T* pData;
};template<typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T> {
public:SquareMatrix() : SquareMatrixBase<T>(n, data.data()) {}...
private:std::array<T, n * n> data;
};
然而這種做法并非永遠能取得優(yōu)勢,硬是綁著矩陣尺寸的那個版本,有可能生成比共享版本更佳的代碼。例如在尺寸專屬版中,尺寸是個編譯期常量,因此可以在編譯期藉由常量的廣傳達到最優(yōu)化;而在共享版本中,不同大小的矩陣只擁有單一版本的函數(shù),可減少可執(zhí)行文件大小,也就因此降低程序的 working set(在“虛內存環(huán)境”下執(zhí)行的進程所使用的一組內存頁),并強化指令高速緩存區(qū)內的引用集中化(locality of reference),這些都可能使程序執(zhí)行得更快速。究竟哪個版本更佳,只能經(jīng)由具體的測試后決定。
同樣地,上面的代碼也使用到了犧牲封裝性的protected
,可能會導致資源管理上的混亂和復雜,考慮到這些,也許一點點模板代碼的重復并非不可接受。
條款 45:運用成員函數(shù)模板接受所有兼容類型
C++ 視模板類的不同具現(xiàn)體為完全不同的的類型(如果用帶有base-derived關系的B、D分別具現(xiàn)化同一個template,產(chǎn)生出來的兩個具現(xiàn)體并不帶有base-derived關系),但在泛型編程中,我們可能需要一個模板類的不同具現(xiàn)體能夠相互類型轉換。
考慮設計一個智能指針類,而智能指針需要支持不同類型指針之間的隱式轉換(如果可以的話),以及普通指針到智能指針的顯式轉換。很顯然,我們需要的是模板拷貝構造函數(shù)(成員函數(shù)模板):
template<typename T>
class SmartPtr {
public:template<typename U>SmartPtr(const SmartPtr<U>& other): heldPtr(other.get()) { ... }template<typename U>explicit SmartPtr(U* p): heldPtr(p) { ... }T* get() const { return heldPtr; }...
private:T* heldPtr;
};
使用get
獲取原始指針,并將在原始指針之間進行類型轉換本身提供了一種保障,如果原始指針之間不能隱式轉換,那么其對應的智能指針之間的隱式轉換會造成編譯錯誤。
智能指針中的shared_ptr支持所有“兼容的內置指針、shared_ptr、auto_ptr和weak_ptr”的構造方法;以及上述除weak_ptr外的其它的賦值操作。(auto_ptr未被聲明為const,是因為當你復制一個auto_ptr時,它其實被改動了)
template<class T>
class shared_ptr
{
public:template<class Y>explicit shared_ptr(Y* p);template<class Y>shared_ptr(shared_ptr<Y> const& r);template<class Y>explicit shared_ptr(weak_ptr<Y> const& r);template<class Y>explicit shared_ptr(auto_ptr<Y> & r);template<class Y>shared_ptr& operator=(shared_ptr<Y> const& r);template<class Y>shared_ptr& operator=(auto<Y> & r);
}
模板構造函數(shù)并不會阻止編譯器暗自生成默認的構造函數(shù),所以如果你想要控制拷貝構造的方方面面,你必須同時聲明泛化拷貝構造函數(shù)和普通拷貝構造函數(shù),相同規(guī)則也適用于賦值運算符:
template<typename T>
class shared_ptr {
public:shared_ptr(shared_ptr const& r); // 拷貝構造函數(shù)template<typename Y>shared_ptr(shared_ptr<Y> const& r); // 泛化拷貝構造函數(shù)shared_ptr& operator=(shared_ptr const& r); // 拷貝賦值運算符template<typename Y>shared_ptr& operator=(shared_ptr<Y> const& r); // 泛化拷貝賦值運算符...
};