C++ 运用成员函数模板接受所有兼容类型

20180402 C++ 运用成员函数模板接受所有兼容类型


所谓智能指针(Smart pointers)是“行为像指针”的对象,并提供指针没有的机能。如std::auto_ptr和tr1::shared_ptr如何能够被用来在正确时机自动删除(heap-based)资源。STL迭代器几乎总是智能指针。


指针可以做的很好的一件事就是:支持隐式转换(implicit conversions),派生类指针可以隐式转换为基类指针,“指向non-const对象”的指针可以转换为“指向const对象”等等。例如以下对象:
class Top{...};
class Middle:public Top{...};
class Bottom:public Middle{...};
Top* pt1 = new Middle;//将Middle* 转换为Top*
Top* pt2 = new Bottom;//将Bottom* 转换为Top*
const Top* pct2 = pt1;//将Top* 转换为const Top*


在用户自定的智能指针中模拟上述转换,我们的理想型是这样:
template<typename T>
class SmartPtr{
public:                   //智能指针通常
  explicit SmartPtr(T* realPtr); //以内置(原始)指针完成初始化
  ...
};




SmartPtr<Top> pt1 =        //将SmartPtr<Middle>转换为
  SmartPtr<Middle>(new Middle);//  SmartPtr<Top>


SmartPtr<Top> pt2 =        //将SmartPtr<Bottom>转换为
  SmartPtr<Bottom>(new Bottom);//  SmartPtr<Top>


SmartPtr<const Top> pct2 = pt1;//将SmartPtr<Top>转换为
                              //  SmartPtr<const Top>


但在同一个模板的不同具现体(instantiations)之间并不存在与生俱来的固有关系(即若以带有base-derived关系的B,D两类型分别具现化某个模板,产生出来的两个具现体并不带有base-derived关系)。
为了获得SmartPtr 类之间的转换能力,我们需明确将其编写出来。








/**********************************************************/
/************模板与泛型编程(Generic Programming)***********/
/**********************************************************/




在上述智能指针实例中,每一个语句创建了一个新式智能指针对象,所以现在我们应该关注如何编写智能指针的构造函数,使其行为能够满足我们的转型需要。我们需要的不是为SmartPtr写一个构造函数,而是为他写一个构造模板,这样的模板(templates)就是所谓的成员函数模板(member function templates,简称member templates),其作用是为类生成函数:
template<typename T>
class SmartPtr{
public:
  template<typename U>   //member template,
  SmartPtr(const SmartPtr<U>& other);  //为了生成拷贝构造函数
  ...
}


上述代码的意思是,对任何类型T和任何类型U,这里可以根据SmartPtr<U>生成SmartPtr<T>,因为SmartPtr<T>有个构造函数接受一个SmartPtr<U>参数。这一类构造函数根据对象u创建对象t(如根据SmartPtr<U>创建一个SmartPtr<T>),而u和v的类型是同一个模板的不同具现体,有时候我们称之为泛化拷贝(generalized copy)构造函数。








假设SmartPtr遵循auto_ptr和tr1::shared_ptr所提供的榜样,也提供一个get成员函数,返回智能指针对象所持有的那个原始指针的副本,那么我们可以在“构造模板”实现代码中约束转换行为,使得它符合我们的预期:
template<typename T>
class SmartPtr{
public:
  template<typename U>
  SmartPtr(const SmartPtr<U>& other)//以otherd heldPtr
    :heldPtr(other.get()){...}    // 初始化this的heldPtr
  T* get() const {return heldPtr;}
  ...
private:
  T* heldPtr;  //这个SmartPtr持有的是内置(原始)指针
};






我使用成员初值列(member initialization list)来初始化SmartPtr<T>的里面的类型为T*的成员变量,并以类型为U*的指针(由SmartPtr<U>持有)作为初值。
这个行为只有当“存在某个隐式转换可将一个U*指针转为一个T*指针”时才能通过编译,而那正是我们想要的,最终效益是SmartPtr<T>现在有了一个泛化拷贝构造函数,这个构造函数只在其所获得的实参隶属适当(兼容)类型时才通过编译。




在类中声明泛化的拷贝构造函数(是一个member template )并不会阻止编译器生成它们自己的拷贝构造函数(一个non-template),所以若你想要控制拷贝构造的方方面面,你必须同时声明泛化拷贝构造函数和正常的拷贝构造函数。相同规则同样适用于赋值(assignment)操作。下面是tr1::shared_ptr的一份定义摘要,eg:


template<class T>
class shared_ptr{
public:
  shared_ptr(shared_ptr const& r);//拷贝构造函数


  template<class Y>
  shared_ptr(shared_ptr<Y> const& r);//泛化拷贝构造函数




  shared+ptr& operator = (shared_ptr const& r);//拷贝赋值


  template<ckass Y>   
  shared_ptr& oparator = (shared_ptr<Y> const& r);//泛化拷贝赋值
  ...
};






注意:
1、请使用成员函数模板(member function templates)生成“可以接受所有兼容类型”的函数
2、若你声明member templates用于"泛化拷贝构造"或"泛化赋值操作",你还需要声明正常的拷贝构造函数和拷贝赋值操作符。
3、

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页