Effective C++

Effective C++是世界頂級C++大師Scott Meyers的成名之作,初版于1991年。在國際上,這本書所引起的反響之大,波及整個計算機技術(shù)出版領(lǐng)域,余音至今未絕。幾乎在所有C++書籍的推薦名單上,這部專著都會位于前三名。

該篇為我在實習(xí)期間學(xué)習(xí)《Effective C++》的一些整理,會每一兩天定期更新。

insert new line 代碼的自動補全問題
Ctrl + . : 參數(shù)提示
Re-Indent 格式化代碼

零、術(shù)語

第0章代碼及注釋:


#ifndef shuyu_h
#define shuyu_h


#endif /* shuyu_h */

typedef int NUM[100];//聲明NUM為整數(shù)數(shù)組類型,可以包含100個元素
NUM n;//定義n為包含100個整數(shù)元素的數(shù)組,n就是數(shù)組名


typedef struct  //在struct之前用了關(guān)鍵字typedef,表示是聲明新類型名
{
    int month;
    int day;
    int year;
} TIME; //TIME是新類型名,但不是新類型,也不是結(jié)構(gòu)體變量名

#include <iostream>

class Shuyu{
    
public:
    Shuyu();  //default 構(gòu)造函數(shù)

    //    explicit可以抑制內(nèi)置類型隱式轉(zhuǎn)換,
    //    所以在類的構(gòu)造函數(shù)中,最好盡可能多用explicit關(guān)鍵字,防止不必要的隱式轉(zhuǎn)換.
    //    顯示轉(zhuǎn)換如 new Shuyu(10);
    explicit Shuyu(int number);
    
    //copy構(gòu)造函數(shù)
    Shuyu(const Shuyu &copy);
    
    /*
     如果 Shuyu s1 = s2; (s2 已經(jīng)被定義)
     那么這個時候會有構(gòu)造函數(shù)被調(diào)用,而不是賦值操作
     
     如果是單純的 s1 = s2 那么此時為賦值操作
     */
    Shuyu& operator=(const Shuyu& copy);
    /*
     運算符重載的部分說明:
     加const是因為:
     ①我們不希望在這個函數(shù)中對用來進行賦值的“原版”做任何修改。
     ②加上const,對于const的和非const的實參,函數(shù)就能接受;如果不加,就只能接受非const的實參。
     
     用引用是因為:
     這樣可以避免在函數(shù)調(diào)用時對實參的一次拷貝,提高了效率。
     copy構(gòu)造函數(shù)是一個比較重要的函數(shù),因為它定義了一個對象如何Pass By Value  即值傳遞
     */  
};

explicit可以抑制內(nèi)置類型隱式轉(zhuǎn)換,
所以在類的構(gòu)造函數(shù)中,最好盡可能多用explicit關(guān)鍵字,防止不必要的隱式轉(zhuǎn)換.

1、typedef:為一種數(shù)據(jù)類型定義一個新名字。
在平臺一上使用typedef long double REAL;,平臺二如果不支持Long Double類型,就改為typedef float REAL,這樣在別的用到REAL的地方就不需要修改了。

  • 理解復(fù)制聲明的技巧:從變量名看起,先往右,再往左
int (*func[5])(int *);

func 右邊是一個[]運算符,說明func是具有5個元素的數(shù)組;func的左邊有一個*,說明func的元素是指針(注意這里的 * 不是修飾func,而是修飾 func[5]的,原因是[]運算符優(yōu)先級比 * 高,func先跟[]結(jié)合)。跳出這個括號,看右邊,又遇到圓括號,說明func數(shù)組的元素是函數(shù)類型的指 針,它指向的函數(shù)具有int * 類型的形參,返回值類型為int。

  • const char * p 的意思是p指向的目標空間的內(nèi)容不可變化,
    char * const p 的意思是指針p的值不可變,但它指向目標的值可變。

2、#define
#define 指令將標識符定義一個程序在編譯的時候會將相同的字符進行替換,也不作正確性檢查,當替換列表中含有多個字符的時候,最好的將替換列表用圓括號括起來。宏定義不是說明或者語句,在行末尾不必添加分號;

  • 在大規(guī)模的開發(fā)過程中,特別是跨平臺和系統(tǒng)的軟件里,define最重要的功能是條件編譯。
#ifdef  windows
...
#else
...
#endif
#ifdef debug
...
...
#endif
  • define與Typedef的區(qū)別
    #define是預(yù)處理指令,在編譯預(yù)處理時進行簡單的替換,不作正確性檢查;
    而typedef是在編譯時處理的,它在自己的作用域內(nèi)給一個已經(jīng)存在的類型一個別名,

本書啟示一:避免不明確(未定義)行為。

其它補充:
char name[] = " hello"
注意name數(shù)組大小為6,別忘了最后的null


一、聯(lián)邦語言C++

C++是一種支持過程形式、面向?qū)ο笮问?、函?shù)形式、泛型形式、元編程形式的語言。

  • a、C:C++是以C為基礎(chǔ)的,block、預(yù)處理器、數(shù)組、指針等都來自于C

  • b 面向?qū)ο?:封裝、繼承、多態(tài)、封裝

  • c Template

-d STL:STL是整個Template程序庫

第一章練習(xí)代碼:
.h文件:

/*
 #define 不被視為語言的一部分  盡量不要用
 #define 不能提供任何的封裝性 即不存在 private #define 一類的東西
 */
//大寫名稱通常用于宏
const double Ratio = 1.65;

//由于常量通常在頭文件內(nèi)部(會被不同的源碼調(diào)用) 因此需要將指針聲明為const
const char* const authorName = "wushuohan";

#include <iostream>


class GamePlayer{
private:
    
    //    頭文件內(nèi)常量聲明
    static const double Ratio;
    
    //a=1是一個聲明式定義
    /*
     通常C++需要一個定義式
     但是如果這個該常量既是static又是整s數(shù)類型 可以忽略
     */
    static const int a = 1;
    
    std::string name;
    std::string age;
    
public:
    /*
     const在*左邊,被指物是常量
     const在*右邊,指針是常量
     */
    void func1(const int * a);
    void func2(int const* a );
    
    //賦值說明  見函數(shù)的實現(xiàn)
    GamePlayer(const std::string &name);
    
    //構(gòu)造函數(shù)的最佳寫法
    GamePlayer(const std::string &name, const std::string &age);
    
    /*
     盡量用local-static代替non-local static
     構(gòu)造順序之Non-local static
     
     函數(shù)內(nèi)的static對象為local-static對象 其余均為non-local
     static對象的析構(gòu)函數(shù)會在main()方法結(jié)束時自動調(diào)用
     
     C++對non-local static的構(gòu)造順序沒有規(guī)定,
     如有需要,可以把他們搬到自己的專屬函數(shù)內(nèi),在函數(shù)內(nèi)部是static,用函數(shù)返回一個reference;
     函數(shù)內(nèi)static對象會在函數(shù)被調(diào)用期間、首次遇上該對象定義時被初始化。
     */
    int test(){
        static int a = 6;
        return  a;
    }
    
    
};


/*
 取一個const的地址是合法的
 但取enum的地址是非法的  指針指不到
 
 單純對于常量  盡量用 const  enum 替換define 可以h降低對預(yù)處理器的需求
 對于函數(shù)形式的宏,用inline函數(shù)來替換
 */

template<typename T>
inline void callWithMax(const T& a , const T& b){
    f(a>b?a:b);
}

/*
 內(nèi)聯(lián)函數(shù)是指那些定義在類體內(nèi)的成員函數(shù),即該函數(shù)的函數(shù)體放在類體內(nèi)。
 
 為什么inline能取代宏?
 1、 inline 定義的類的內(nèi)聯(lián)函數(shù),函數(shù)的代碼被放入符號表中,在使用時直接進行替換,(像宏一樣展開),沒有了調(diào)用的開銷,效率也很高。
 2、 類的內(nèi)聯(lián)函數(shù)也是一個真正的函數(shù),編譯器在調(diào)用一個內(nèi)聯(lián)函數(shù)時,會首先檢查它的參數(shù)的類型,保證調(diào)用正確。然后進行一系列的相關(guān)檢查。這樣就消除了它的隱患和局限性。
 3、 inline 可以作為某個類的成員函數(shù),當然就可以在其中使用所在類的保護成員及私有成員。
 
 
 宏是由預(yù)處理器對宏進行替代,而內(nèi)聯(lián)函數(shù)是通過編譯器控制來實現(xiàn)的
 */




class textBlock{
public:
    //成員函數(shù)length()不該動對象內(nèi)的任何一個Bit
    std::size_t length() const;
    //當const和Non-const有著相同的實現(xiàn)時,讓non-const調(diào)用const
    
private:
    
    //mutable定義的成員變量總是可能會更改,即使是在Const函數(shù)中
    mutable bool lengthIsValid;
    mutable std::size_t textLength;
};

內(nèi)聯(lián)函數(shù)是指那些定義在類體內(nèi)的成員函數(shù),即該函數(shù)的函數(shù)體放在類體內(nèi)。

為什么inline能取代宏?
1、 inline 定義的類的內(nèi)聯(lián)函數(shù),函數(shù)的代碼被放入符號表中,在使用時直接進行替換,(像宏一樣展開),沒有了調(diào)用的開銷,效率也很高。
2、 類的內(nèi)聯(lián)函數(shù)也是一個真正的函數(shù),編譯器在調(diào)用一個內(nèi)聯(lián)函數(shù)時,會首先檢查它的參數(shù)的類型,保證調(diào)用正確。然后進行一系列的相關(guān)檢查。這樣就消除了它的隱患和局限性。
3、 inline 可以作為某個類的成員函數(shù),當然就可以在其中使用所在類的保護成員及私有成員。

.cpp文件

#include "part1.h"
#include <iostream>

//實現(xiàn)文件常量定義
const double GamePlayer::Ratio = 1.5;

GamePlayer::GamePlayer(const std::string &name){
    //注意:這里是賦值  不是對Name的初始化
    //初始化應(yīng)該在進入構(gòu)造函數(shù)之前就發(fā)生了
    (*this).name = name;
}

//構(gòu)造函數(shù)的最佳寫法 這樣 構(gòu)造函數(shù)不需要執(zhí)行賦值操作
//先設(shè)新值再賦值太浪費了
/*注意:C++兩個成員的初始化順序也有差異  先base 再derived
 這里先name,再age
 */

GamePlayer::GamePlayer(const std::string &name,
                       const std::string &age):name(name),age(age){}

std::size_t textBlock::length()const{
    //具體略
    return NULL;
}

二、構(gòu)造/析構(gòu)/賦值運算

2.1、構(gòu)造

編譯器會自動為一個類聲明一個copy構(gòu)造函數(shù)、一個copy assignment操作符、一個析構(gòu)函數(shù)。如果沒有聲明任何構(gòu)造函數(shù),那么編譯器會聲明一個default 構(gòu)造函數(shù)。

注意:C++ 不允許讓reference改指向不同的對象
本章練習(xí)代碼

#include <iostream>


template <class T>
class NamedObject{
public:
    NamedObject(std::string &name,const T & value);
    
    
private:
    
    //注意:C++ 不允許讓reference改指向不同的對象
    //因此如果需要用 object1 = object2 時 需要自己定義一個copy assignment操作符
    std::string& nameValue;
    
    //同時如果類內(nèi) 內(nèi)置了 const成員 編譯器不會生成賦值函數(shù) 因為修改const是不合法的
    const T objectValue;
    
    
    //在private里聲明 copy構(gòu)造和copy assignment可以防止編譯器自行創(chuàng)建
    //而只聲明不定義 是為了防止friend可以調(diào)用他們
    //這樣就阻止了編譯器自動創(chuàng)建這些函數(shù)了
    NamedObject(const NamedObject&);
    NamedObject& operator=(const NamedObject&);//沒有定義
};

2.2、析構(gòu)

補充:C++的三種訪問權(quán)限

三種訪問權(quán)限

  • public:可以被任意實體訪問
  • protected:只允許子類及本類的成員函數(shù)訪問
  • private:只允許本類的成員函數(shù)訪問

三種繼承方式:public、private、protected
1、public繼承不改變基類成員的訪問權(quán)限
2、private繼承使得基類所有成員在子類中的訪問權(quán)限變?yōu)閜rivate
3、protected繼承將基類中public成員變?yōu)樽宇惖膒rotected成員,其它成員的訪問 權(quán)限不變。
4、基類中的private成員不受繼承方式的影響,子類永遠無權(quán)訪問。
帶有多態(tài)性質(zhì)的base-class必須聲明一個virtual的析構(gòu)函數(shù),如果class的設(shè)計目的不是base-class,即不聲明。

若 TimerKeeper * pw = new AtomicClock();
而此時 基類TimerKeeper的析構(gòu)函數(shù)是non-virtual的
那么derived-class會經(jīng)由based-class的析構(gòu)函數(shù)銷毀
那么derived-class的derived部分就不會被銷毀
這種局部銷毀會導(dǎo)致資源泄露

本章代碼:

class TimerKeeper {
private:
    
public:
    TimerKeeper();
    
    //錯誤的寫法  ---如果用作繼承的話
//    ~TimerKeeper();
    
    virtual ~TimerKeeper();
};


/*
 若 TimerKeeper *pw = new AtomicClock();
 而此時 基類TimerKeeper的析構(gòu)函數(shù)是non-virtual的
 那么derived-class會經(jīng)由based-class的析構(gòu)函數(shù)銷毀
 那么derived-class的derived部分就不會被銷毀
 這種局部銷毀會導(dǎo)致資源泄露
 
 解決方案:析構(gòu)函數(shù)前+virtual關(guān)鍵字
 */
class AtomicClock:public TimerKeeper{
public:
        void close();
    
};


/*如果class不帶virtual,通常說明它不被意圖用作一個基類
 這時不應(yīng)將它的析構(gòu)函數(shù)聲明為virtual
 
 因為會多些帶一個virtual tavle pointer 來決定哪個方法被調(diào)用
 會增加對象的體積
 */



class AbstractTest{
public:
    
    //聲明為抽象類  即該類不能創(chuàng)建對象
    //最深層的derived-class的析構(gòu)會先被調(diào)用,其次是每一個based-class的析構(gòu)
    //因此除了聲明外,還需要對這個抽象類的析構(gòu)函數(shù)創(chuàng)造一個定義。
    virtual ~AbstractTest()=0;
};




//析構(gòu)函數(shù)絕對不要吐出異常
//如果需要對某個異常的情況作出反應(yīng),那么可以在class內(nèi)寫一個普通函數(shù)執(zhí)行該操作
class DBCon {
private:
    AtomicClock ac;
    bool closed;
    

public:
    //有效的異常處理方法  定義自己的close 讓異常 有處可尋
    void close(){
        ac.close();
        closed = true;
    }
    
    ~DBCon(){
        
        if(!closed){   //如果客戶端不關(guān)閉的g話
            try {
                ac.close();//可能會拋出異常
            } catch (int e) {
                std::abort();
                /*
                 abort強迫結(jié)束程序
                 阻止異常從析構(gòu)函數(shù)傳播出去
                 
                 當然可以不使用abort,在catch后吞下異常,讓程序在遭遇錯誤后繼續(xù)執(zhí)行
                 */
            }
        }}
};
  • 析構(gòu)函數(shù)絕對不要吐出異常

  • 如果需要對某個異常的情況作出反應(yīng),那么可以在class內(nèi)寫一個普通函數(shù)執(zhí)行該操作

  • 條款9:在析構(gòu)和構(gòu)造時不要調(diào)用virtual函數(shù),因為這類調(diào)用從不下降至derived-class這一層。

  • 令賦值操作符返回一個reference to this *

2.3、自動賦值出現(xiàn)的問題

class BitMap {
    
};


//解決自我賦值的問題
//可能會出現(xiàn)指針指向一塊已經(jīng)被釋放過的地址
class WidGet {
private:
    BitMap *bp;
    
public:
    WidGet& operator=(const WidGet &rhs){
       /*
        如果是同一個對象
        delete bp刪除的既是this的bp又是rhs的bp
        因此需提前加一個證同測試
        */
//        if(this == &rhs)return *this;
        
        
        //更好的方法是不去理會 而創(chuàng)建一個新的拷貝
        BitMap * bptemp = bp;
        
    
        bp = new BitMap(*rhs.bp);  //拷貝構(gòu)造
        delete bptemp;
        return *this;
    }
};

三、資源管理

3.1、以對象管理資源

如果在……發(fā)生了異常、return、goto等語句,那么delete將不會執(zhí)行。
解決方法是將資源放進對象,對象的析構(gòu)函數(shù)會自動釋放這些資源。
本章代碼1:
兩種智能指針 以及 隱式轉(zhuǎn)換Operator

//智能指針的原理是,接受一個申請好的內(nèi)存地址,構(gòu)造一個保存在棧上的智能指針對象,
//當程序退出棧的作用域范圍后,由于棧上的變量自動被銷毀,智能指針內(nèi)部保存的內(nèi)存也就被釋放掉了

//智能指針會自動銷毀它指向的對象,所以不能同時指向同一個對象

//為防止資源泄露 請使用智能指針對象  它們將在構(gòu)造函數(shù)中獲得資源并在析構(gòu)函數(shù)中釋放資源
class Investment{
public:
    int daysHeld(const Investment*);
    bool isTaxFree();
    
    Investment* createInvestMent(){
        /*在堆中分配*/
        Investment *invest = new Investment();
        return invest;
    }
    
    
    /*
     如果在……發(fā)生了異常、return、goto等語句,那么delete將不會執(zhí)行。
     解決方法是將資源放進對象,對象的析構(gòu)函數(shù)會自動釋放這些資源。
     */
    void func1(){
        Investment *pInv = createInvestMent();
        /*
         調(diào)用指針……
         */
        delete pInv;
    }
    
    
    //許多動態(tài)資源被分配后都用于單一的區(qū)塊和函數(shù)中
    //auto_ptr 智能指針
    void funcAdvice(){
        //createInvestment()的返回結(jié)果會作為智能指針的初值
        std::auto_ptr<Investment> pInv(createInvestMent());
        
        /*
         像原來一樣調(diào)用指針……
         */
    }//會由智能指針的析構(gòu)函數(shù)釋放掉資源
    
    
    void funcAdviceSecond(){
//        shared_ptr也是一個智能指針,使用引用計數(shù),每一個shared_ptr的拷貝都指向相同的內(nèi)存。
//        每使用一次,內(nèi)部的引用計數(shù)加1,每析構(gòu)一次,內(nèi)部的引用計數(shù)減1,減為0時,刪除所指向的堆內(nèi)存。
//        相比之前的auto_ptr多了拷貝構(gòu)造  但不能打破環(huán)狀引用
        std::shared_ptr<Investment> pInv1(createInvestMent());
        std::shared_ptr<Investment> pInv2(pInv1);
//        ……………
    }//pInv1 pInv2被銷毀了
    
    int test(){
        //shared_ptr不會進行隱式轉(zhuǎn)換 需用構(gòu)造
        std::shared_ptr<Investment> pInv(createInvestMent());
        
//        直接傳原始指針
        int days = daysHeld(pInv.get());
        
        bool tax = pInv->isTaxFree();
        bool tax1 =(*pInv).isTaxFree();
        if(tax==tax1){
            return days;
        }
        return 0;
    }
    
//    隱式轉(zhuǎn)換 operator + 返回結(jié)果 可以根據(jù)需要自動改變類型 但是不太安全
    operator double()const{
//        …………
        return 0;
    }
    
private:
};

本章代碼2:
定義刪除器

//每個人的地址有四行,每行是一個string
//在delete中也要使用delete[]
typedef std::string AddressLines[4];


/*
 注意:
 auto_ptr 與 shared_ptr都在其析構(gòu)函數(shù)內(nèi)做delete  而不是delete[]
 */
class Lock{
public:
    int priority();
    void processWidget(std::shared_ptr<int> pw,int priority);
    
    void test1(){
        
        // int 類型指針的賦值
        int *a;
        *a= 5;
        
        int b = 5;
        a = &b;
        
//        注意  構(gòu)造函數(shù)里面為指針類型
        //使用分離語句 務(wù)必以獨立的new 語句將對象存入智能指針
        //如果不這么做,將下面兩句寫成一句 一旦異常發(fā)生 可能會有難以察覺的內(nèi)存泄漏
        std::shared_ptr<int> pw(a);
        processWidget(pw,priority());
        
        
        
    }
    
    void lock(int *);
    static void unLock(int *);
    
    //注意初始化 與 賦值 的區(qū)別 ptr先變成pm 然后再上鎖
    //shared_ptr的第二個參數(shù)是刪除器,當引用計數(shù)為0時就會調(diào)用
    explicit Lock(int *pm):sharedPt(pm,unLock){
        lock(sharedPt.get());
    }
    
    
private:
    //智能指針的復(fù)制很有可能不合理
    //方法1  把copy構(gòu)造放在private里 不去定義
    Lock(const Lock &);
    Lock& operator=(const Lock &);
    
    
    //方法二 使用shared_ptr  并且指定 刪除器
    std::shared_ptr<int> sharedPt;
};

四、設(shè)計與聲明

注意:絕對不要返回一個reference 指向一個 local-棧 對象 或者返回reference指向 堆-allocated 對象

本章代碼如下:

#include <iostream>

//類定義里面的成員變量和函數(shù)默認都是private型 類本身默認為public型
class Window {
    
public:
    std::string name()const;
    virtual void display()const;
    /*
     Window的copy構(gòu)造會被調(diào)用 w會被初始化
     當isOK返回時w會被銷毀
     */
    bool isOK(Window w);
    
//    可以用pass-by reference to const來避免這些構(gòu)造和析構(gòu)
    bool isOkAdvice(const Window& w);
    
};

class WindowWithScrollBars:public Window {
    
public:
    void display()const;
    
    void printAndDisplay(const Window& w){
        std::cout<<w.name();
        w.display();
    }
    
    //對于內(nèi)置類型 如(int),以及STL  通常用pass—By-value比較合適
};


/*
 注意:絕對不要返回一個reference 指向一個 local-棧 對象
 或者返回reference指向 堆-allocated 對象
 */
class Rational {
private:
    int n,d;
    
    /*
     錯誤的寫法
     result是一個local對象,會在函數(shù)退出前被銷毀
     因此返回的reference指向舊的Rational
     */
//    const Rational& operator *(const Rational & lhs,
//                               const Rational & rhs){
//        Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
//        return result;
//    }
//

    /*
     注意區(qū)別:
     上面的Rational沒有使用new 關(guān)鍵字
     它在??臻g創(chuàng)建對象
     函數(shù)退出時棧內(nèi)存會被回收
     
     而下面的Rational采用了new 只要是new就在堆空間分配
     記住一個死規(guī)則 只要是new 就需要delete
     */
    
    
    /*
     更垃圾的寫法
     而這里new了以后,內(nèi)存無人來釋放delete
     
     */
//    const Rational& operator *(const Rational & lhs,
//                               const Rational & rhs){
//        Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
//        return  result;
//    }
    
    //友元函數(shù)可以在類內(nèi)的任何地方聲明 不受域的影響
    friend const Rational operator * (const Rational & lhs,
                                      const Rational & rhs);
    
public:
    Rational(int a= 0, int b =0);
};

從封裝性來看,只有兩種封裝性,private和其它。
如果有一個public或者protected的成員變量被更改,會有不可預(yù)知的大量代碼被更改。

切記將成員變量聲明為private,這可以提供客戶訪問的一致性。
如果某些東西被封裝,它們便不再可見。它可以使我們改變事物而只影響有限客戶。愈多的函數(shù)可以訪問數(shù)據(jù),它的封裝性越差


偏特化:

函數(shù)模板沒有偏特化,因為有函數(shù)重載的概念,C++根據(jù)參數(shù)的類型來判斷重載哪一個函數(shù),如果還進行偏特化,這就與重載相沖突。但是,我們可一個對模板進行重載,從而實現(xiàn)偏特化。

模板的實例化類型確定是在編譯期間

練習(xí)代碼:

/*
 模板的實例化類型確定是在編譯期間
 
 全特化一般用于處理有特殊要求的類或者函數(shù),此時的泛型模板無法處理這種情況。
 
 模板為什么要特化,因為編譯器認為,
 對于特定的類型,如果你對某一功能有更好地實現(xiàn),那么就該聽你的。
 
 C++只允許對class template偏特化
 對function-template的偏特化是不合法的
 
 模板實例化只會實例化用到的部分,沒有用到的部分將不會被實例化
 */


//原始的模版
template <typename T,typename  T1>
class Test{
    
public:
    bool compare(T& a,T &b){
        return (a<b)?true:false;
    }
};


//全特化  可以看作是一種重載
template <>//參數(shù)都指定了 所以參數(shù)列表為空
class Test<int,char> {
public:
    bool compare(int &a,char &b){
        return false;
    }
};


//偏特化  只指定部分的參數(shù)類型
template <typename T>
class Test<int,T>{
public:
    bool compare(int & a,T& b){
        return false;
    }
};


//注意 函數(shù)沒有偏特化  只有全特化

//函數(shù)模板沒有偏特化,因為有函數(shù)重載的概念,C++根據(jù)參數(shù)的類型來判斷重載哪一個函數(shù),如果還進行偏特化,這就與重載相沖突。但是,我們可一個對模板進行重載,從而實現(xiàn)偏特化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容