0
点赞
收藏
分享

微信扫一扫

模板与继承

鲤鱼打个滚 2023-06-09 阅读 94
c++

1.1 命名模板参数优化

许多模板类有非常多的参数例如:


template <class T1 = BreadPolicy1,
         class T2 = BreadPolicy2,
         class T3 = BreadPolicy3,
         class T4 = BreadPolicy4>
class BreadSlicer{
...
}

但在大多情况下,模板类都有默认的参数,但是如果我想修改其中的一个参数,例如我要修改T3的默认参数那么我就得知道T1,T2,因为输入缺省参数得知道前面所有参数得值。
有以下的编程技巧可以解决这个问题。

核心思路思路是通过一个辅助类PolicySelector将所有的变量类型封装到其中,下面是步骤

#include <iostream>

class Policy1{

};

class Policy2{
public:
    static void do_print(){
        std::cout << "Policy2 do print" <<std::endl;
    }
};

class Policy3{
	static void do_print(){
        std::cout << "Policy3 do print" <<std::endl;
    }
};

class Policy4{

};
// 假设上面的类使我们最终要传递的模板参数。

class DefaultPolicy{
public:
    typedef Policy1 p1;
    typedef Policy2 p2;
    typedef Policy3 p3;
    typedef Policy4 p4;
};
// 首先通过DefaultPolicy 将这4类型都重新命名到一个类中,最终直接通过p1,p2,p3,p4完成操作


class BreadPolicy :virtual public DefaultPolicy{
};
// BreadPolicy 用于作为默认参数


template <class T>
class Policy1_is : virtual public DefaultPolicy{
public:
    typedef T p1;
};

template <class T>
class Policy2_is : virtual public DefaultPolicy{
public:
    typedef T p2;
};

template <class T>
class Policy3_is : virtual public DefaultPolicy{
public:
    typedef T p3;
};

template <class T>
class Policy4_is : virtual public DefaultPolicy{
public:
    typedef T p4;
};

//Policy1_is, Policy2_is, Policy3_is, Policy4_is 这四个类中都有p1,p2,p3,p4这四个类型,然后将外部新定义的类型覆盖之前定义的。 这里需要使用虚拟继承,原因后面说


template <class Base, int N>
class Discriminator : public Base{

};

template <class T1,
         class T2,
         class T3,
         class T4>
class PolicySelector : public Discriminator<T1, 1>,
                       public Discriminator<T2, 2>,
                       public Discriminator<T3, 3>,
                       public Discriminator<T4, 4>
{
};

template <class T1 = BreadPolicy,
         class T2 = BreadPolicy,
         class T3 = BreadPolicy,
         class T4 = BreadPolicy>
class BreadSlicer{
public:
    void print(){
        policies::p2::do_print();
        policies::p3::do_print();
    }
private:
    typedef PolicySelector<T1, T2, T3, T4> policies;
};

class PolicyP2{
public:
    static void do_print(){
        std::cout << "test  policy p2" << std::endl;
    }
};

class PolicyP3{
public:
    static void do_print(){
        std::cout << "test  policy p3" << std::endl;
    }
};



int main(){
    BreadSlicer test;
    test.print();
    std::cout << std::endl;

    BreadSlicer<Policy2_is<PolicyP2>> test1;
    test1.print();
    std::cout << std::endl;

	BreadSlicer<Policy2_is<PolicyP3>, Policy3_is<PolicyP2>> test2;
    test2.print();
    std::cout << std::endl;
}

在最终的调用这BreadSlicer 中,将传入的所有模板参数重新用一个类保管,所有的参数类型都被封装到了 PolicySelector该类初始化的时候会依次继承传入的类,所有在上文中BreadPolicy和PolicyP… 都是用的是virtual,这样会避免重复继承。
当使用默认参数得时候,看上去是继承类四次,但实际上

首先BreadSlicer<Policy2_is<PolicyP2>> test1; 这样的传参实际上就是就是传入类第一个参数,其他三个参数用的都是默认值,之后PolicySelector分别继承Discriminator 1,2,3,4, 由于Policy2_isBreadPolicy 都是派生类,它们里面都拥有P1, P2,P3,P4,这样最后在继承Discriminator的时候将新传入的参数覆盖掉了之前的参数。

Policy2 do print
Policy3 do print

test  policy p2
Policy3 do print

test  policy p3
test  policy p2


进程已结束,退出代码0


上述代码运行结果
上面的方法成功解决了填写一个默认参数得问题,但是如果想随意填写两个默认参数,那么就会出现问题,只有

1.2 空基类优化

在C++中如果一个类没有任何成员变量,那么编译器会将其大小设置为1。
但是如果有下面的情况:

template<class T1, class T2>
class Opt{
public:
    T1 info1; //模板参数作为成员变量,可能为null, 那么它大小为1会占用一个字节
    T2 info2; //又会占用一个字节
};

如果T1和T2都是空类,那么Opt 就会是2个字节,也许我们可以

class opt :private T1, private T2 {
 ...
}

也许可以这样,但是T2,和T2还有别的可能,如果是int, 或者不是class,那么上面的写法不成立

假设我们知道一个参数必然是class,而另外一个参数不是类,那可以有以下的方法


template<class Base, class type>
class BaseMemberPair : private Base{
private:
    type member_;
public:
    BaseMemberPair(Base const& base, type member)
        : Base(base), member_(member)
    {}

    const Base& first() const{
        return (Base* const &) *this;
    }

    Base& first() {
        return (Base* &) *this;
    }

    const type& Second() const {
        return member_;
    }

    type& Second() {
        return member_;
    }
};

template<class T1, class T2>
class Option{
public:
    Option(T1& t, T2 val)
    :info_and_storage(t, val)
    {
    }

private:
    BaseMemberPair<T1, T2> info_and_storage;
};

将这两个参数封装到BaseMemberPair 中,因为有成员变量的存在,BaseMemberPair不会为空类,从而消除了可能存在的额外空间使用。

int main(){
    std::cout << sizeof(Opt<Empty2, Empty2>) << std::endl;
    Empty ss;

    Option<Empty, int> cc(ss, 1);
    Opt<Empty, int> aa;

    std::cout << sizeof(cc) << std::endl;
    std::cout << sizeof(aa) << std::endl;
}
/*
2
4
8
*/

最终结果中,使用Option更加省空间, 一个对象省类4bt的空间。在Opt的大小为8, 应该是存在内存对其的问题。不过不管怎么样确实省下了字节

举报

相关推荐

0 条评论