现在的位置: 首页 > 综合 > 正文

C++总结之模版和泛型编程

2013年01月22日 ⁄ 综合 ⁄ 共 3718字 ⁄ 字号 评论关闭

三年来的实践,遇到的模版编程并不是很多,只是在最近的一个电信项目中,看到了高超的模版技术应用,十分感慨,这里对于模版的总结,完全参照Effective C++ 第三版。

隐式接口和编译期多态
1.classes和template都支持接口(interfaces)和多态(polymorphism)。

2.对于classes而言接口是显式的(explicit),以函数签名为中心。多态则是通过virtual函数发生在运行期。
3.对于template参数而言,接口是隐式的(implicit),奠基于有效表达式。多态则是通过template实例化和函数重载解析(function overloading resolution)发生于编译期。

了解typename
1.声明template参数时,前缀关键字class和typename可以互换。
2.需要使用关键字typename标识嵌套从属类型名称;但是不能在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class的修饰符。

所谓从属名称,就是在template内出现的依赖于某个template参数的名称。
关于typename,如下
template<typename T>
class Derived : public Base<T>::Nested // base class list中不允许出现"typename"
{
public:
    explicit Derived(int x)
     : Base<T>::Nested(x)     // mem.ini.list中不允许"typename"
    {
        typename Base<T>::Nested temp; //嵌套从属类型名称,需要加上"typename"修饰
        ... ...
    } 
};

3. 处理模版化基类内的名称
当模版存在继承的时候,一定会导致继承自基类的名称无法通过编译,有三个方法可以解决该问题:
我们假设有这样一个基类模版
template<typename T>
class MsgSender
{
public:
    ... ...
   
    void sendClear(const MsgInfo & info) // 继承类中将要调用的函数
    {
         // send message.
         std::string msg = info.message();
         T object;
         object.sendText(msg);
    } 
 
};
当继承类需要使用函数sendClear时,编译时无法通过的,需要做如下选择:
1.在base class函数调用动作之前加上”this->”:
template<typename T>
class LoggingMsgSender : public MsgSender<T>
{
public:
    ... ...
   
    void sendClearMsg(const MsgInfo & info)
    {
        // do something first
        this->sendClear(info); //使用“this->”的调用
        // do something finally
    }
};

2.使用using声明式可以做到:
template<typename T>
class LoggingMsgSender : public MsgSender<T>
{
public:
    using MsgSender<T>::sendClear; // 告诉编译器,假设sendClear位于base class内
    ... ...
   
    void sendClearMsg(const MsgInfo & info)
    {
        // do something first
        sendClear(info); // 这里可以调用成功
        // do something finally
    }
};
这里并不是base class内的名称被derived class名称遮掩,而是编译器根本就不进入base class作用域内查找,我们需要通过using告诉编译器,请它那么做。

3.明白指出被调用函数位于base class内:
template<typename T>
class LoggingMsgSender : public MsgSender<T>
{
public:
    ... ...
   
    void sendClearMsg(const MsgInfo & info)
    {
        // do something first
        MsgSender<T>::sendClear(info); // 直接调用
        // do something finally
    }
};
这种做法还是欠妥,因为如果被调用的是个virtual函数,上述的明确资格修饰会关闭”virtual绑定行为”。

将与参数无关的代码抽离template
1.template会生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。
2.因非类型参数模版而造成的代码膨胀,往往可以消除,做法就是以函数参数或者class成员变量替换template参数。
3.因类型参数(type parameters)而造成的代码膨胀,往往可以降低,做法是让带有完全二进制表述的具现类型共享实现码。

运用成员函数模版接受所有兼容类型
1.请使用member function template(成员函数模版)生成“可接受的所有兼容类型”的函数。
2.如果我们声明member template用于“泛化 copy函数”或“泛化assignment操作”,那么还需要声明正常的copy函数和copy assignment操作符。

需要类型转换时请为模版定义非成员函数
当我们编写一个class template,而它所提供的“与此template相关的”函数支持“所有参数的隐式转换”时,请将那些函数定义为“class template内部的friend函数”。
典型的例子是:
template<typename T> class Rational; // the Rational template.
//helper template
template<typename T>
const Rational<T> doMultiply(const Rational<T> & lhs, const Rational<T> & rhs);

// template class Rational definition
template<typename T>
class Rational
{
public:
    ... ...

    friend const Rational<T> operator *(const Rational<T> & lhs, const Rational<T> & rhs)
    {
      
return doMultiply(lhs,rhs);
    }
};

使用traits classes表现类型信息
1.traits classes使得“类型相关信息”在编译期可用。
他们以template和“templates特化”完成实现。
如何设计并实现一个traits classes
首先,确认若干你希望将来可取得的类型相关信息;
其次,为该信息选择一个名称;
第三,提供一个template和一组特化版本,内含我们希望支持的类型信息。
2.整合重载技术后,traits classes有可能在编译期对类型进行测试。
如何使用一个traits classes
首先,建立一组重载函数或函数模版,彼此间的差异只在于各自的traits参数。令每个函数实现码与其接受的traits信息相符合;
其次,建立一个控制函数或函数模版,它调用上述那些函数并传递traits class所提供的信息。

认识template元编程
Template metaprogramming(TMP)是编写template-based C++程序并执行于编译期的过程。
TMP两大效力:第一,它让某些事情更加容易。如果没有它,那些事情将是困难的,甚至是不可能的。第二,由于template metaprogramming执行于C++编译期,因此可将工作从运行期转移至编译期。这将导致一个结果是,某些错误原本通常在运行期才能侦测到,现在可在编译期找出来。另一个结果是,使用TMP的C++程序可能在每一个方面都更高效:较小的可执行文件,较短的运行期,较少的内存需求。
TMP的一个典型例子是求阶乘:
template<unsigned n>
struct Factorial {
    enum{ value = n * Factorial<n-1>::value };
};

template<>
struct Factorial<0> {
    enum { value = 1 };
};

使用方式如下:
Factorial<10>::value; 这样就可以得到10的阶乘了

 

主线:《Effective C++》 Third Edition

-- 转载请注明出处

抱歉!评论已关闭.