• Item 45:使用成员函数模板来接受全部兼容的类型


    Item 45: Use member function templates to accept “all compatible types”.

    Item 13提到智能指针可用来自己主动释放堆中的内存,STL中的迭代器也是一种智能指针,它甚至支持链表元素指针的++操作。 这些高级特性是普通指针所没有的。本文以智能指针为例。介绍成员函数模板的使用:

    • 成员函数模板能够使得函数能够接受全部兼容的类型。
    • 假设你用成员函数模板声明了拷贝构造函数和赋值运算符,仍然须要手动编写普通拷贝构造函数和拷贝运算符。

    隐式类型转换

    智能指针尽管比普通指针提供了很多其它实用的特性,但也存在一些问题。比方我们有一个类的层级:

    class Top{};
    class Middle: public Top{};
    class Bottom: public Middle{};
    

    普通指针能够做到派生类指针隐式转换为基类指针:

    Top *p1 = new Bottom;
    const Top *p2 = p1;
    

    但假设是智能指针,比方我们实现了SmartPtr,我们则须要让以下代码经过编译:

    SmartPtr<Top> p1 = SmartPtr<Bottom>(new Bottom);
    SmartPtr<const Top> p2 = p1;
    

    同一模板的不同实例之间是没有继承关系的,在编译器看来AutoPtr<Top>AutoPtr<Bottom>是全然不同的两个类。

    所以上述代码直接编译是有问题的。

    重载构造函数

    为了支持用SmartPtr<Bottom>初始化SmartPtr<Top>,我们须要重载SmartPtr的构造函数。 原则上讲,有多少类的层级我们就须要写多少个重载函数。

    由于类的层级是会扩展的,因此须要重载的函数数目是无穷的。

    这时便能够引入成员函数模板了:

    template<typename T>
    class SmartPtr{
    public:
        template<typename U>
        SmartPtr(const SmartPtr<U>& other);
    };
    

    注意该构造函数没有声明为explicit。是为了与普通指针拥有相同的使用风格。子类的普通指针能够通过隐式类型转换变成基类指针。

    接受同一模板的其它实例的构造函数被称为通用构造函数(generalized copy constructor)。

    兼容类型检查

    其实,通用构造函数提供了很多其它的功能。他能够把一个SmartPtr<Top>隐式转换为SmartPtr<Bottom>,把一个SmartPtr<int>转换为SmartPtr<double>

    但普通指针是不同意这些隐式转换的。因此我们须要把它们禁用掉。注意一下通用构造函数的实现方式便能够解决问题:

    template<typename T>
    class SmartPtr{
    public:
        template<typename U>
        SmartPtr(const SmartPtr<U>& other): ptr(other.get()){};
        T* get() const{ return ptr; }
    private:
        T *ptr;
    };
    

    ptr(other.get())时编译器会进行类型的兼容性检查。仅仅有当U能够隐式转换为T时。SmartPtr<U>才干够隐式转换为SmartPtr<T>。 这样就避免了不兼容指针的隐式转换。

    其它使用方式

    除了隐式类型转换。成员函数模板还有别的用途,比如赋值运算符。。以下是shared_ptr的部分源代码:

    template<class T> 
    class shared_ptr{
    public:
        template<class Y>
            explicit shared_ptr(Y *p);
        template<class Y>
            shared_ptr<shared_ptr<Y> const& r>;
        template<class Y>
            shared_ptr& operator=(shared_ptr<Y> const& r);
    };
    

    能够看到普通指针Y*shared_ptr<Y>声明了explicit,须要显式的类型转换;而shared_ptr<Y>之间仅仅须要隐式转换。 使用拷贝构造函数模板存在一个问题:编译器是会生成默认的拷贝构造函数?还是会从你的模板实例化一个拷贝构造函数? 即Y == T场景下的编译器行为。

    其实,成员函数模板不会改变C++的规则

    C++规则讲:假设你没有声明拷贝构造函数,那么编译器应该生成一个。

    所以Y == T时拷贝构造函数不会从成员函数模板实例化,而是会自己生成一个。

    所以shared_ptr模板中还是手动声明了拷贝构造函数:

    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<class Y>
            shared_ptr& operator=(shared_ptr<Y> const& r);
    };
    
  • 相关阅读:
    01-移动端 REM 适配(postcss-pxtorem,lib-flexible的使用)
    19-webpack性能优化集锦
    10-map/WeakMap/WeakSet的使用场景
    03-web worker vue项目实战
    ELFhash
    哈希查找
    Logger之Logger.getLogger(CLass)使用(转载)
    mybatis中多对一查询
    IDEA中无法打开查看log文件解决方案
    Self-Supervised Scene De-occlusion(转载)
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7065846.html
Copyright © 2020-2023  润新知