模板与继承

news/2024/11/25 9:38:57/

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 printtest  policy p2
Policy3 do printtest  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, 应该是存在内存对其的问题。不过不管怎么样确实省下了字节


http://www.ppmy.cn/news/287874.html

相关文章

Sony Xperia L36h 4.0通用一键root(无需解锁)详细图文教程

本文提供Sony Xperia L36h 4.0通用一键root&#xff08;无需解锁)详细图文教程,更多安卓手机系统刷机教程敬请关注安卓网新手教程站。 1.工具下载,文章末尾提供下载 rootingKit_for_XPERIA_ICS和超级用户 Su朱雀网络 www.zhuquewl.com ROM包下载per User(解压即得)&#xff0c;…

手机支持linux,看看你的手机是否支持Ubuntu Touch?

上周&#xff0c;Ubuntu Touch为Galaxy Nexus、Nexus 4、Nexus 7和Nexus 10推上了开发者预览版&#xff0c;让用户和开发人员可以对该系统的核心和应用先睹为快&#xff0c;比如摄像和Wi-Fi等普通功能。当然&#xff0c;开发者预览版还有不少bug&#xff0c;因此不建议作为日常…

因电平匹配导致的adc采样偏差

本文中使用的芯片为 华大 HC32L110C4&#xff0c;adc精度为12位 在做一个电流采样计算时发现adc数据一直有偏差&#xff0c;经过查找后&#xff0c;发现问题出在串口的电平匹配上&#xff0c;因为串口 RX 收到的电压信号过高&#xff0c;导致adc的电源基准变化&#xff0c;导致…

基于LM2733升压电路设计

简介 LM2733是TI公司的具有40V内部FET开关的升压变换器&#xff0c;其封装为SOT-23极小封装器件&#xff0c;开关频率有0.6MHz和1.6MHz两个版本&#xff0c;该芯片具有如下特性。 • 40V DMOS FET 开关 • 1.6MHz&#xff08;"X"&#xff09; 、 0.6MHz&#xff0…

使用JLink烧写MM32程序

学习目标&#xff1a; 最近一两年&#xff0c;STM32芯片价格暴涨。为了降低产品成本&#xff0c;选择了国产MM32芯片。在某宝上买了JLINK烧录器烧写单片机程序。支持国产&#xff01;&#xff01;&#xff01; 学习内容&#xff1a; 一、安装JLink软件 1、下载JLINK软件 J…

android手机进入fastboot,安卓手机进入Fastboot模式的多种操作方法

对于智能手机用户&#xff0c;我们通常会对手机进行刷机处理&#xff0c;而传统手机刷机方式为Fastboot模式&#xff0c;也就是我们经常说的线刷模式&#xff0c;用户进入Fastboot模式的方式有许多中&#xff0c;可通过手动进入、第三方工具进入、当然你也可以使用ADB命令进入&…

手机root总结/索尼L36h手机root

方法二&#xff1a;使用卓大师刷机软件&#xff08;测试过可行&#xff09; 方法一&#xff1a;&#xff08;刚买来的时候有用&#xff0c;升过一次级后就无效了&#xff09; http://dl.vmall.com/c0gyjazbvq 下载此工具&#xff0c;执行root.bat即可

l36h android 4.4,操作更接近Z2 索尼Xperia Z安卓4.4体验

索尼Xperia Z L36h对于索尼用户来说或许有着非常与众不同的意义&#xff0c;还记得2013年CES中这款手机登场的时候&#xff0c;凭借全新的设计语言以及黑白紫这三种索尼旗舰机型的标准配色&#xff0c;索尼Xperia Z在一时间吸引了不少人的目光。这款手机承载着索尼的多个第一&a…