effective c++学习笔记五

 

effective c++学习笔记五

 
 
模板与泛型编程
 
c++ template自身是一个完整的图灵机:可以用来计算任何可计算的值。
=>模板元编程
=>创造出在c++编译器内执行并与编译完成时停止执行的程序
 
 
 
条款41:了解隐式接口和编译期多态
 
面向对象编程总是用显式接口和运行期多态解决问题
泛型编程除了显示接口和运行期多态外,更重要的是隐式接口和编译期多态。
 
运行期多态和编译期多态之间的差异 类似于哪个重载函数被调用和哪一个virtual函数被绑定之间的差异。
显示接口是由函数的签名式,也就是函数名称、参数类型、返回类型构成
隐式接口并不基于函数签名式,而是由有效表达式组成
 
 
class和template都支持接口和多态
class多态通过virtual函数发生在运行期
template通过具现化和函数重载解析,发生于编译期
 
 
 
 
条款42:了解typename的双重意义
 
当声明template参数类型时,class和typename没什么不同。
template<class T>class Widget;
template<typename T>class Widget;
 
 
但是并不是任何时候都等价
template<typename C>
void print2nd(const C& container)
{
    if(container.size()>=2)
    {
        C::const_iterator iter(container.begin());//compile error
        ++iter;
        int value = *iter;
        std::cout<<value;
    }
}

 

template内出现的名称如果相依于某个template参数,称之为从属名称,如果从属名称在class内呈现嵌套装,我们称为嵌套从属名称,也就是嵌套从属名称并且涉及某类型。
嵌套从属名称可能导致解析困难。
如C::const_iterator *x;
当我们知道c是什么之前,就不知道const_iterator 是一个值还是一个类型。
c++中的规则是,如果解析器在template中遭遇一个嵌套从属名称,便假设这个名称不是一个类型,除非你告诉他是,缺省情况下不是类型,因此要写成
typename C::const_iterator iter......
 
一般性规则:如果你要再template中涉及一个嵌套从属名称,就必须在紧邻他的前一个位置放上关键字typename
例外:typename不可以出现在base classes list内的嵌套从属类型之前,也不可以在成员初始列中作为base class的修饰符
template<typename T>
class D:public B<T>::nested
{
    public:
    explicit D(int x):B<T>::nested(x)
    {
        typename B<T>::nested temp;
        ......
    }
}

 

 

 

 
条款43:学会处理模板化基类内的名称
class MsgInfo{};
template<typename Company>
class MsgSender
{
    public:
    void sendClear(const MsgInfo &info)
    {
        string msg;
        Company c;
        c.sendCleartext(msg);
    }
    void sendSecret(const MsgInfo& info){}
};
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
    public:
    sendClearMsg(const MsgInfo& info)
    {
        ...
        sendClear(info);//compile error
        ...
    }
};

 

 
问题在于,当编译器遭遇到LoggingMsgSender时不知道Company是什么参数,因此也不会知道MsgSender<Company>看起来像什么,因此就不知道他是不是有个sendClear函数。
 
c++知道基类可能被特化,而那个特化的版本可能不提供和一般性template相同的接口。因此他往往拒绝在模板化基类内寻找继承而来的名称。
就某种意义而言,当从面向对象的c++进入template c++继承就不像那么畅通无阻了
 
有三种解决方案:
1.在base class函数调用前加上this->
2.使用using声明式 usingMsgSender<Company>::sendClear;
3.明确指出被调用函数位于基类内:MsgSender<Company>::SenderClear(info);
第三种方式不能用于调用virtual函数
这三种方法都对编译器承诺:基类template任何特化版本都将支持其一般(泛化)版本所提供的接口
 
 
 
 
条款44:将于参数无关的代码抽离template
 
共性与变性分析:做简单的分析 避免重复。感受当template被具现化多次时可能发生的重复
template生成多个类和多个函数,因此任何template代码都不应该与某个造成膨胀的template参数产生相依关系。
因非类型模板参数而造成的 代码膨胀,往往可以消除,做法是以函数或者类成员变量替换template参数。
因类型参数而造成的代码膨胀,往往可以降低,做法是让带有完全相同二进制表述的具现类型共享实现代码。
 
 
 
 
条款45:运用成员函数模板接受所有兼容类型
 
所谓智能指针式是行为像指针的对象,并且提供指针没有的机能。
STL中的迭代器几乎都是智能指针。
 
同一个template的不同具现体之间并不存在什么与生俱来的固有关系,即:如果以带有继承关系的两个类分别具现化某个template,产生出的两个具现体并不带有继承关系。
如果需要获得指针指向的对象的类型转换能力,我们必须将他们明确的编写出来。
 
恒源函数模板并不改变语言基本规则,在一个类内部声明泛化拷贝构造函数不会阻止编译器生成他们自己的拷贝构造函数。因此需要同时声明一个非泛化的拷贝构造函数。相同的规则也适用于赋值操作。
 
 
 
 
条款46:需要类型转换时请为模板定义非成员函数
 
为了让类型转换可能发生在所有的实参身上,我们需要一个非成员函数,为了令这个函数可以自动具现化,我们需要将它申明在类的内部,而解决这个矛盾的唯一方式是友元函数。
 
 
 
 
条款47:请用traits classes表现类型信息
 
traits是一种技术,基本要求是对内置类型和用户自定义类型的表现必须一样好。
因为要支持内置类型,因此不可以出现类型内的嵌套信息。
因此类型的信息必须在类型自身信息之外,标准技术是把它放进一个template及其一个或者多个特化版本中。
习惯上都被实现为structs。
 
设计方式:
1.确认若干你希望将来可取得的类型相关信息
2.为该信息确定一个名称
3.提供一个template和一组特化版信息,内涵你需要支持的类型相关信息
 
使用方式:建立一组重载函数,彼此之间的差异只在于各自的traits参数。令每个函数实现码与其接受之traits信息相应和。
建立一个控制函数或者函数模板,它调用上述函数并且传递traits所提供的信息。
 
traitsclasses使得类型相关信息在编译期可用,他们以template和template特化完成实现
整合重载技术后,traits classes有可能在编译器对类型执行if else测试
 
 
 
 
条款48:认识template元编程
 
template metaprogramming(TMP)模板元编程时编写template c++程序并且执行于编译器的过程。
是c++写成 并且执行于c++编译器内的程序。
 
TMP已经证明是一个图灵完全的机器,可以计算任何可以计算的事物。
TMP没有真正的循环,所有的循环都是由递归完成。
 
 
 
posted @ 2012-09-06 14:24  w0w0  阅读(218)  评论(0编辑  收藏  举报