Effective C++读书笔记 第七部分 模板与泛型编程

来源:互联网 发布:人工智能 sci期刊 编辑:程序博客网 时间:2024/05/22 08:01

条款41:了解隐式接口和编译期多态

(1) class和templates都支持接口(interfaces)和多态(polymorphism)。
(2) 对classes而言接口是显式的(explicit),以函数签名为中心。多态则是通过virtual函数发生于运行期。
(3) 对template参数而言,接口是隐式的(implicit),奠基于有效表达式;多态则是通过template具现化和函数重载解析(function overloading resolution)发生于编译期。

条款42:了解typename的双重定义

(1) 声明template参数时,前缀关键字class与typename可互换。
例如:

template <class T> //or template <typename T>
 
void swap(T& obj1, T& obj2) {
 
  T temp(obj1);
 
  obj1 = obj2;
 
  obj2 = temp;
 
}

(2) 请使用关键字typename标识嵌套从属类型名称;但不得在base class lists(基类列)或member initailization list(成员初值表列)内以作为base class修饰符。
例如,你必须:

tempalte <typename C> 
void print2nd(const C& container)//打印容器内的第二元素 

  if( containter.size() >= 2 ){ 
   typename C::const_iterator iter( containter.begin() ); 
   ++iter; 
   int value = *iter; 
   std::cout << value; 
  } 
}

解释:template内出现的名称如果相依于某个template参数,称之为从属名称;如果从属名称在class内呈嵌套状,称为嵌套从属名称。在上面的例子中,C::const_iterator就是嵌套从属名称。编译器并不知道 const_iterator是个类型,除非你告诉编译器,不然它以为这是C中的static成员变量或者是global变量。
但需要注意一下情况:

template <typename T> 
class Derived:public Base<T>::Nested{ //base class list中不允许出现"typename" 
 public: 
  explicit Dervied(int x) : Base<T>::Nested(x){ //成员初始化列表中不允许"typename" 
  typename Base<T>::Nested temp; //既不在base class list也不在初始化列表中,作为一个base class修饰符需加上typename.
   ... 
  } 
  ... 
};

条款43:学习处理模板化基类内的名称

    本条款给出了以下问题的解决方案:当基类是模板化的类时,派生类应该怎样调用基类中的函数。

template<typename Company> 
class MsgSender{ 
 public: 
  ... 
  void sendClear(const MsgInfo& info){ 
   std::string msg; 
   ...//根据info产生信息 
   Company c; 
   c.sendClearText(msg); 
  } 
  void sendSecret(const MsgInfo& info){...} //这里调用的是c.sendEncrypted. 
};
 
template <typename Company> 
class LoggingMsgSender:public MsgSender<Comany>{ 
 public: 
  ... 
  void sendClearMsg(const MsgInfo& info){ //为避免"名称遮掩"现象的发生,采用了一个不同的名称
   ...// record status information before sending message 
   sendClear(info); 
   ...//record status information after sending message. 
  } 
  ... 
};

以上代码直接编译会报错:抛出了”sendClear不存在”的抱怨。解决方法有以下三个:

(1) 在base class函数调用动作之前加上”this->”:

template <typename Company> 
void LoggingMsgSender<Company>::sendClearMsg(const MsgInfo& info){ 
  ...
   this->sendClear(info); //ok 
  ... 
}

(2) 使用using声明式:

template <typename Company> 
class LoggingMsgSender:public MsgSender<Company>{ 
 public: 
  //这里的情况不是base class名称被derived class名称遮掩,而是编译器不进入base base
 
  //作用域查找,于是我们通过using声明式告诉它,请它这么做
 
  using MsgSender<Company>::sendClear;//告诉编译器,请它假设sendClear位于base class内 
  ...
 
  void sendClearMsg(const MsgInfo& info){ 
   ... 
   sendClear(info);//ok 
   ... 
  } 
};

(3) 明明白白指出被调用函数位于base class内:

template <typename Company> 
class LoggingMsgSender:public MsgSender<Company>{ 
 public: 
  ... 
  void sendClearMsg(const MsgInfo& info){ 
  ... 
  MsgSender<Company>::sendClear(info); //ok 
  ... 
 } 
 ... 
};

条款44:将与参数无关的代码抽离template

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

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

举个例子,假设现在你要为固定尺寸的矩阵编写一个template类,该类声明要支持矩阵的逆运算,可以采用下面代码:

template <typename T, std::size_t n> //矩阵元素类型T,尺寸大小为n 
class SquareMatrix{ 
 public: 
   ... 
   void invert(); //逆运算 
};

这样定义,声明以下两个对象会产生不同的代码,造成代码膨胀:

SquareMatrix<double,5>square1;

SquareMatrix<double,10>square2;

减小代码膨胀的方法是采用以下定义:

template <typename T > //矩阵元素类型T 
class SquareMatrix{ 
 public: 
  ... 
  void invert(std::size_t n); //把尺寸大小n作为参数 
};

条款45:运用成员函数模板接受所有兼容类型

本条款告诉你,怎样编写成员函数模板。从下面例子说起:

怎样支持以下操作:

template <typename T> 
class SmartPtr{ 
 public: 
  explicit SmartPtr(T* realPtr);//智能指针通常以原始指针完成初始化
  ... 
};
 
SmartPtr<Top> top1_smart_ptr = SmartPtr<Middle>(new Middle); 
SmartPtr<Top> top2_smart_ptr = SmartPtr<Bottom>(new Bottom); 
SmartPtr<const Top> const_top2_ptr = top1_smart_ptr;

一个比较好的方案是:

//根据SmartPtr<U>创建一个SmartPtr<T>,其中T是基类,U是T的派生类 
template <typename T> 
class SmartPtr{ 
 public: 
  template <typename U> 
  SmartPtr(const SmartPtr<U>& other) :held_ptr_( other.get() ){...} //这里就完成子类向父类的隐式转换过程. 
  T* get()const{ return held_ptr_;} 
  ... 
 private: 
  T* held_ptr_; //这是SmartPtr持有的内置指针. 
};

上述中的SmartPtr构造函数便是成员函数模板(member function template),得出的结论是:

(1) 请使用memberfunction template(成员函数模板)生成”可接受所有兼容类型”的函数。

(2)如果你声明member template用于”泛化copy构造”或”泛化assignment操作”,你还需要声明正常copy构造函数和copyassignment操作符。(不然编译器会为你生成默认的copy构造函数和copy assignment操作符)

条款46:需要类型转换时请为模板定义非成员函数

本条款告诉程序员,当你需要进行类型转化时,为了避免麻烦,最好将模板定义为非成员函数(如friend函数)。

条款47: 请使用traits classes 表现类型信息

条款48: 认识template 元编程



原创粉丝点击