标题上说“将与参数无关的代码抽离template”,这里的参数既可以指类型,也可以是非类型,我们先来看看非类型的情况。

假定我们要为矩阵写一个类,这个矩阵的行列元素个数相等,是一个方阵,因而我们可以对之求逆运算。因为方阵的元素可以有多种类型,同时方阵的维数(方阵大小)也可以不同,像下面这样,我们使用了模板:

 template <class T, size_t n>
class SquareMatrix
{
public:
void Invert();
}; int main()
{
SquareMatrix<int, > a;
SquareMatrix<int, > b;
}

模板既可以指定类型,也可以指定其他参量,比如这里的size_t,用来表示方阵的维数。程序看起来是没有问题的,编译也是通过的,但从代码优化上看,还是有空间可以做的。a和b虽然都是元素为int型的方阵,但因为方阵维数不同,因而生成了两个Invert函数,这两个Invert函数的代码只是在循环个数上不同(取觉于方阵维数n),但算法思想完全是一样的。

为了防止编译器生成冗余的代码,我们可以将Invert抽离这里,变成一个以n为参数的函数,像下面这样:

 class SquareMatrixBase
{
public:
SquareMatrixBase(T* p) : DataPointer(p){}
void Invert(size_t n){}
private:
T* DataPointer;
}; template <class T, size_t n>
class SquareMatrix: private SquareMatrixBase<T>
{
public:
SquareMatrix() : SquareMatrixBase(Data)
{}
void Invert()
{
SquareMatrixBase::Invert(n);
}
private:
T Data[n * n];
};

这里我们定义了一个BaseSquareMatrix类,这个类可以视为一个工具类,因为SquareMatrix是private继承于它的,我们把算法一致、只与维数n相关的算法函数都放在BaseSquareMatrix中,且将n作为它的Invert()函数的形参,为了使BaseSquareMatrix可以访问数据,从而求逆运算,我们声明了它的成员指针,这个指针将会在构造时指向子类的数据存储空间。

子类SquareMatrix没有大变化,只是在Invert的时候,用了一句代码就搞定了——调用父类的Invert函数,并把维数n传过去。

这样在main函数中,针对类型都是int,但矩阵维数不同的情况,Invert中生成的冗余代码非常少(只有一句话),父类也因为模板参数与维数无法,所以也只生成一份。从这个角度来看,将与参数无关的代码抽离template起到了减少代码冗余度的优化作用。

目前只是说将与类型无关的代码进行抽离(造一个父类,把算法相同的部分提取出来,放到父类),对于不同类型,其实也可以通过void*来实。比如STL,要想令我们使用list<int*>,list<const int*>生成的代码冗余度保持很低,这就需要在相应的函数里面,将之链到父类的一个公共实现版本里面,这个版本的形参是void*。

最后总结一下:

1. Template生成多个classes与多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。

2. 因非类型模板参数而造成的代码膨胀,往往可以消除,做法是以函数参数或者class成员变量替换template参数。

3. 因类型而造成的代码膨胀,也可以降低,做法是让带有完全相同二进制表述的具现类型共享实现码。

读书笔记_Effective_C++_条款四十四:将与参数无关的代码抽离template的更多相关文章

  1. 读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承

    这个条款书上内容说的篇幅比较多,但其实思想并不复杂.只要能理解三句话即可,第一句话是:纯虚函数只继承接口:第二句话是:虚函数既继承接口,也提供了一份默认实现:第三句话是:普通函数既继承接口,也强制继承 ...

  2. 读书笔记_Effective_C++_条款二十四: 若所有参数皆需类型转换,请为此采用non-member函数

    class A { private: int a; public: A(int x) :a(x){} A operator*(const A& x) { return A(a*x.a); } ...

  3. 读书笔记_Effective_C++_条款二十九:为“异常安全”而努力是值得的

    还是举书上的例子: void PrettyMenu::changeBackground(std::istream& imgSrc) { lock(&mutex); delete bgI ...

  4. 读书笔记_Effective_C++_条款三十:了解inline的里里外外

    学过基本程序课的同学都知道,inline是内联的关键字,它可以建议编译器将函数的每一个调用都用函数本体替换.这是一种以空间换时间的做法.把每一次调用都用本体替换,无疑会使代码膨胀,但可以节省函数调用的 ...

  5. 读书笔记_Effective_C++_条款三十九:明智而审慎地使用private继承

    private继承的意义在于“be implemented in turns of”,这个与上一条款中说的复合模型的第二层含义是相同的,这也意味着通常我们可以在这两种设计方法之间转换,但书上还是更提倡 ...

  6. 读书笔记_Effective_C++_条款三十六:绝不重新定义继承而来的non-virtual函数

    这个条款的内容很简单,见下面的示例: class BaseClass { public: void NonVirtualFunction() { cout << "BaseCla ...

  7. 读书笔记_Effective_C++_条款三十二:确定你的public继承继承塑模出is-a关系

    这一条款是说的是公有继承的逻辑,如果使用继承,而且继承是公有继承的话,一定要确保子类是一种父类(is-a关系).这种逻辑可能与生活中的常理不相符,比如企鹅是生蛋的,所有企鹅是鸟类的一种,直观来看,我们 ...

  8. 读书笔记_Effective_C++_条款二十八:避免返回handlers指向对象内部成分

    举个例子: class Student { private: int ID; string name; public: string& GetName() { return name; } } ...

  9. 读书笔记_Effective_C++_条款二十六:尽可能延后变量定义式的出现时间

    这个条款从字面意思还是很好理解的,就是在使用这个变量前才去定义,而不是很早就定义了它,而在很后面的时候才去使用.这个条款只适用于对变量声明位置没有要求的语言,比如C++.对于像C或者一些脚本语言,语法 ...

  10. 读书笔记_Effective_C++_条款二十五: 考虑写出一个不抛出异常的swap函数

    在之前的理论上调用对象的operator=是这样做的 void swap(A& x) { std::swap(a, x.a); } A& operator=(const A& ...

随机推荐

  1. atitit.js 各版本 and 新特性跟浏览器支持报告

    atitit.js 各版本 and 新特性跟浏览器支持报告 一个完整的JavaScript实现是由以下3个不同部分组成的 •核心(ECMAScript)--JavaScript的核心ECMAScrip ...

  2. paip.java 开发中web server的选择jboss resin tomcat比较..

    paip.java 开发中web server的选择jboss resin tomcat比较.. 作者Attilax  艾龙, EMAIL:1466519819@qq.com 来源:attilax的专 ...

  3. JSTL核心标签库使用

    JSTL是什么? java standard taglib(java标准标签库). 标签是什么? sun公司制订的一种技术规范,用来将jsp页面中的java代码使用类似于html当中的标记来替换 的一 ...

  4. Leetcode 299 Bulls and Cows 字符串处理 统计

    A就是统计猜对的同位同字符的个数 B就是统计统计猜对的不同位同字符的个数 非常简单的题 class Solution { public: string getHint(string secret, s ...

  5. python中x的平方

    x ** 2 sqdEvens = [x ** 2 for x in range(8) if not x % 2] for i in sqdEvens: print(i) 0 4 16 36 > ...

  6. python元类

    第一个实例: # coding=utf8 # 手动创建,返回一个类对象 MyShinyClass = type('MyShinyClass', () ,{}) print(MyShinyClass) ...

  7. 在主方法中定义一个大小为50的一维整型数组,数组i名为x,数组中存放着{1,3,5,…,99}输出这个数组中的所有元素,每输出十个换一行

    package hanqi; import java.util.Scanner; public class Test7 { public static void main(String[] args) ...

  8. Windows 8.1/Server 2012 R2/Embedded 8.1 with Update 3(MSDN最新版)

    微软于12月16日更新了包含Update 3的ISO,此次更新并不会明显改善用户的界面体验,下载后请校验MD5.我整理了中.英文的8.1/服务器版/嵌入式版本/多国语言包. 1,Windows 8.1 ...

  9. DotNetMQ的一个小demo

    DotNetMQ是一个新的.独立的.开源的,完全基于C#和.NET Framework3.5的消息队列系统 下载源代码 - 1.28 MB 下载二进制文件 - 933 KB 下载例子 - 534 KB ...

  10. iOS RSA 证书加密

    #import "GLQyRsa.h" #import "GLSupprot.h" #import "GLLoginViewController.h& ...