模板与继承之艺术——空基类优化

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,转载请注明出处。
原文地址:https://www.cnblogs.com/claruarius/p/4111581.html