程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Item 45:使用成員函數模板來接受所有兼容的類型

Item 45:使用成員函數模板來接受所有兼容的類型

編輯:C++入門知識

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 p1 = SmartPtr(new Bottom);
    SmartPtr p2 = p1;
    

    同一模板的不同實例之間是沒有繼承關系的,在編譯器看來AutoPtrAutoPtr是完全不同的兩個類。 所以上述代碼直接編譯是有問題的。

    重載構造函數

    為了支持用SmartPtr初始化SmartPtr,我們需要重載SmartPtr的構造函數。 原則上講,有多少類的層級我們就需要寫多少個重載函數。因為類的層級是會擴展的,因此需要重載的函數數目是無窮的。 這時便可以引入成員函數模板了:

    template
    class SmartPtr{
    public:
        template
        SmartPtr(const SmartPtr& other);
    };
    

    注意該構造函數沒有聲明為explicit,是為了與普通指針擁有同樣的使用風格。子類的普通指針可以通過隱式類型轉換變成基類指針。

    接受同一模板的其他實例的構造函數被稱為通用構造函數(generalized copy constructor)。

    兼容類型檢查

    事實上,通用構造函數提供了更多的功能。他可以把一個SmartPtr隱式轉換為SmartPtr,把一個SmartPtr轉換為SmartPtr。 但普通指針是不允許這些隱式轉換的。因此我們需要把它們禁用掉。注意一下通用構造函數的實現方式便可以解決這個問題:

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

    ptr(other.get())時編譯器會進行類型的兼容性檢查,只有當U可以隱式轉換為T時,SmartPtr才可以隱式轉換為SmartPtr。 這樣就避免了不兼容指針的隱式轉換。

    其他使用方式

    除了隱式類型轉換,成員函數模板還有別的用途,例如賦值運算符。。下面是shared_ptr的部分源碼:

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

    可以看到普通指針Y*shared_ptr聲明了explicit,需要顯式的類型轉換;而shared_ptr之間只需要隱式轉換。 使用拷貝構造函數模板存在一個問題:編譯器是會生成默認的拷貝構造函數?還是會從你的模板實例化一個拷貝構造函數? 即Y == T場景下的編譯器行為。

    事實上,成員函數模板不會改變C++的規則。C++規則講:如果你沒有聲明拷貝構造函數,那麼編譯器應該生成一個。 所以Y == T時拷貝構造函數不會從成員函數模板實例化,而是會自己生成一個。

    所以shared_ptr模板中還是手動聲明了拷貝構造函數:

    template
    class shared_ptr{
    public:
        shared_ptr(shared_ptr const& r);
        template
            shared_ptr(shared_ptr const& r);
    
        shared_ptr& operator=(shared_ptr const& r);
        template
            shared_ptr& operator=(shared_ptr const& r);
    };

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved