模板與繼承之藝術——空基類優化,模板繼承藝術優化
1、概念
C++中有“空”類的概念,這就意味著在運行期間其內部不好任何內存。
只包含類型的成員函數、非虛成員函數和靜態數據成員的類為空類。
非靜態的數據成員,虛函數和虛基類則在運行時期消耗存儲空間。
2、空基類優化如下:
#include<iostream>
using namespace std;
class Empty{
typedef int Int;
};
class EmptyToo : public Empty
{};
class EmptyThree : public EmptyToo
{};
int main()
{
cout << "sizeof(Empty):" << sizeof(Empty) << endl;
cout << "sizeof(EmptyToo):" << sizeof(EmptyToo)<< endl;
cout << "sizeof(EmptyThree):" << sizeof(EmptyThree) << endl;
}
(1)在類EmptyToo中類Empty沒有分配存儲空間
(2)帶有優化空基類的空類作為被繼承的基類(
沒有其他基類時,單繼承)時,其大小也為0,這也就是EmptyTree 能和Empty具有相同大小的原因所在。
3、如果有其他基類時:
#include<iostream>
class Empty{
typedef int Int;
};
class EmptyToo : public Empty{};
class EmptyThree : public Empty, public EmptyToo{};
int main()
{
cout << "sizeof(Empty):"<< sizeof(Empty) << endl; //輸出1
cout << "sizeof(EmptyToo)" << sizeof(EmptyToo)<< endl; //輸出1
cout << "sizeof(EmptyThree):" << sizeof(EmptyThree) << endl; //輸出2
}
如果繼承時是多重繼承:
EmptyThree中有兩個不同的子類Empty對象,一個是直接繼承來的Empty對象,另一個是EmptyToo繼承來的Empty對象。
由於是兩個對象所以不能讓其有相同的地址空間。即這裡優化(2)不起作用,EmptyThree是兩個Empty和EmptyToo大小之和。
4、將成員作為基類
只舉一例,模板參數確定會被實例化為類型(不是非類型,不是原生類型比如int),該模板類的另一個成員類型不是空類。如下:
template<typename CustomClass>
class Optimizable{
private:
CustomClass info;
void* storage;
};
改寫為:
template <typename CustomClass>
class Optimizable {
private:
BaseMemberPair<CustomClass, void*> info_and_storage;
};
//
template<typename Base, typename Member>
class BaseMemberPair : private Base{
private:
Member member;
public:
BaseMemberPair(Base const &b, Member const &m):Base(b), member(m){}
//通過first來訪問基類數據
Base const& first() const{ return (Base const&)*this; }
Base& first() const { return (Base&)*this; }
//通過second()來訪問成員變量
Member const& second() const { return this->member; }
Member& second() const { return this->member; }
};
優化前:
#include<iostream>
using namespace std;
template<typename CustomClass>
class Optimizable{
private:
CustomClass info;
void* storage;
};
class Test{
};
int main()
{
cout << sizeof(Optimizable<Test>) << endl;
//結果為8
return 0;
}
View Code
優化後:
#include<iostream>
using namespace std;
template<typename Base, typename Member>
class BaseMemberPair : private Base{
private:
Member member;
public:
BaseMemberPair(Base const &b, Member const &m):Base(b), member(m){}
//通過first來訪問基類數據
Base const& first() const{ return (Base const&)*this; }
//提供給const對象調用
Base& first() { return (Base&)*this; }
//通過second()來訪問成員變量
Member const& second() const { return this->member; }
Member& second() { return this->member; }
};
template <typename CustomClass>
class Optimizable {
private:
BaseMemberPair<CustomClass, void*> info_and_storage;
};
class Test{
};
int main()
{
cout << sizeof(Optimizable<Test>) << endl;
//結果為4
return 0;
}
View Code
5、警告:
針對下面的情況:
template<typename T1, typename T2>
class MyClass{
private:
T1 a;
T2 b;
};
//優化
template<typename T1, typename T2>
class MyClass : private T1, private T2{};
在不知道T1和T2是否為類型的情況下,最好不要盲目使用上面的優化方法,因為:
(1)此方法不適用於原生類型int等
(2)如果T1, 和T2的類型相同繼承會出問題。
(3)增加基類會改變接口,因為多重繼承,如果T1和T2都有共同的基類Base的話,那麼MyClass中的數據成員,成員函數將會有二義性。所以此方法最好適應是之前提演示出的情況。
編輯整理:Claruarius,轉載請注明出處。