溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

數(shù)據(jù)結(jié)構(gòu)(02)_模板庫(kù)的基礎(chǔ)設(shè)施搭建

發(fā)布時(shí)間:2020-07-31 14:30:43 來(lái)源:網(wǎng)絡(luò) 閱讀:617 作者:三九感冒靈 欄目:編程語(yǔ)言

1.泛型編程簡(jiǎn)介

1.1.引言

數(shù)據(jù)額結(jié)果課程專注于數(shù)據(jù)元素之間的關(guān)系,和數(shù)據(jù)元素的操作方法,并不關(guān)系具體的數(shù)據(jù)類型,所以選擇支持泛型編程的語(yǔ)言最為合適數(shù)據(jù)結(jié)構(gòu)課程的學(xué)習(xí)。

1.2.泛型編程的概念

不考慮具體的數(shù)據(jù)類型的編程方式稱為泛型編程,舉例,對(duì)于swap函數(shù)考慮下面的泛型寫法。

void swap(T a, T b)
{
    T t = a;
    a = b;
    b = t
}

這里的T不是指具體的數(shù)據(jù)類型,而是泛指任意的數(shù)據(jù)類型。在C++語(yǔ)言中泛型編程通過(guò)模板實(shí)現(xiàn)。

1.3.C++的函模板模板

函數(shù)模板是一種特殊的函數(shù),可以使用不同類型進(jìn)行調(diào)用,看起來(lái)和普通函數(shù)很相似,區(qū)別是類型可以被參數(shù)化。
語(yǔ)法規(guī)則:
數(shù)據(jù)結(jié)構(gòu)(02)_模板庫(kù)的基礎(chǔ)設(shè)施搭建
函數(shù)模板的使用有兩種方式:
數(shù)據(jù)結(jié)構(gòu)(02)_模板庫(kù)的基礎(chǔ)設(shè)施搭建

1.4.C++類模板

以相同的方式處理不同的類型,在類聲明前使用template進(jìn)行標(biāo)識(shí)。
數(shù)據(jù)結(jié)構(gòu)(02)_模板庫(kù)的基礎(chǔ)設(shè)施搭建
類模板的應(yīng)用:
只能顯示的指定具體的類型,無(wú)法自動(dòng)推導(dǎo),使用具體類型定義對(duì)象:
數(shù)據(jù)結(jié)構(gòu)(02)_模板庫(kù)的基礎(chǔ)設(shè)施搭建
編程實(shí)驗(yàn):

#include <iostream>

using namespace std;

template <typename T>
void Swap(T& a, T& b)
{
    T t = a;
    a = b;
    b = t;
}

template <typename T>
class Op
{
public:
    T process(T v)
    {
        return v * v;
    }
};

int main()
{
    int a = 2;
    int b = 1;

    Swap(a, b);

    cout << "a = " << a << " " << "b = " << b << endl;

    double c = 0.01;
    double d = 0.02;

    Swap<double>(d, c);

    cout << "c = " << c << " " << "d = " << d << endl;

    Op<int> opInt;
    Op<double> opDouble;

    cout << "5 * 5 = " << opInt.process(5) << endl;
    cout << "0.3 * 0.3 = " << opDouble.process(0.3) << endl;

    return 0;
}

2.智能指針

2.1.內(nèi)存泄漏

動(dòng)態(tài)內(nèi)存申請(qǐng)后,用完后不歸還會(huì)導(dǎo)致內(nèi)存泄漏;C++語(yǔ)言中沒有垃圾回收機(jī)制,指針無(wú)法控制所執(zhí)行的堆空間的生命周期。

2.2.智能指針

使用指針對(duì)象代替原生指針,這樣在指針生命周期結(jié)束時(shí),可以自動(dòng)調(diào)用析構(gòu)函數(shù),歸還對(duì)象所使用的堆空間
實(shí)現(xiàn)思路:重載指針操作符( *和-> )
一片堆空間只能由一個(gè)指針類標(biāo)識(shí),杜絕指針運(yùn)算(重載拷貝構(gòu)造函數(shù)、和賦值操作符完成堆空間所有權(quán)的轉(zhuǎn)接)
編程實(shí)驗(yàn)

#ifndef SMARTPOINTER_H
#define SMARTPOINTER_H

namespace DTLib
{
template<typename T>
class SmartPointer
{
protected:
    T* m_pointer;

public:
    SmartPointer(T* p =NULL)
    {
        m_pointer = p;
    }

    SmartPointer(const SmartPointer<T>& obj)
    {
        m_pointer = obj.m_pointer;
        const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
    }

    SmartPointer<T>& operator=(const SmartPointer<T>& obj)
    {
        if(this != &obj)
        {
            delete m_pointer;
            m_pointer = obj.m_pointer;
            const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
        }
        return *this;
    }

    T* operator->()
    {
        return m_pointer;
    }

    T& operator*()
    {
        return *m_pointer;
    }

    bool isNULL()
    {
        return(m_pointer == NULL);
    }

    T* get()
    {
        return m_pointer;
    }

    ~SmartPointer()
    {
        delete m_pointer;
    }

};

}

#endif // SMARTPOINTER_H

3.C++異常簡(jiǎn)介

C++中的異常處理:
try處理正常邏輯、throw用于拋出異常、catch用于捕獲異常
如果一個(gè)異常沒有被處理,會(huì)沿著函數(shù)的調(diào)用棧向上傳播,直至被處理,或著程序異常終止。
catch捕獲異常時(shí)會(huì)嚴(yán)格匹配,不進(jìn)行任何形式的轉(zhuǎn)換,catch(…)用與捕獲所有異常,放在最后,每一個(gè)異常只能被捕獲一次
父子兼容原則適用、所以捕獲子類的異常在上、父類在下(子類對(duì)象可以看做一個(gè)父類對(duì)象)
編程實(shí)驗(yàn):

#include <iostream>

using namespace std;

double divide(double a, double b)
{
    const double delta = 0.000000000000001;
    double ret = 0;

    if( !((-delta < b) && (b < delta)) ) {
        ret = a / b;
    }
    else {
        throw 0;   // 產(chǎn)生除 0 異常
    }

    return ret;
}

void Demo1()
{
    try
    {
        //throw 3;
        //throw 5.0;
        throw 'c';
    }
    catch(int i)
    {
        cout << "catch(int i)" << endl;
    }
    catch(double d)
    {
        cout << "catch(double d)" << endl;
    }
    catch(char c)
    {
        cout << "catch(char c)" << endl;
    }
}

void Demo2()
{
    //throw 0.0001;
    //throw "D.T.Software";//const char*

}

int main()
{
    cout << "main() begin" << endl;

    try
    {
        double c = divide(1, 1);

        cout << "c = " << c << endl;
    }
    catch(...)
    {
        cout << "Divided by zero..."  << endl;
    }

    Demo1();

    try
    {
        Demo2();
    }
    catch(char* c)
    {
        cout << "catch(char* c)" << endl;
    }
    catch(const char* cc)
    {
        cout << "catch(char* cc)" << endl;
    }
    catch(...)
    {
        cout << "catch(...)" << endl;
    }

    cout << "main() end" << endl;

    return 0;
}

4.異常類構(gòu)建

4.1. 標(biāo)準(zhǔn)庫(kù)異常類族

現(xiàn)代C++庫(kù)必然包含重要的異常類族
數(shù)據(jù)結(jié)構(gòu)(02)_模板庫(kù)的基礎(chǔ)設(shè)施搭建

4.2. 自定義異常類族

數(shù)據(jù)結(jié)構(gòu)(02)_模板庫(kù)的基礎(chǔ)設(shè)施搭建
異常類中的接口定義:

#define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__))
class Exception
{
protected:
    char* m_message;
    char* m_location;
public:
    void init(const char* message, const char* file, int line);
    Exception(const char* message);
    Exception(const char* file, int line);
    Exception(const char* message, const char* file, int line);
    Exception(const Exception& e);
    Exception& operator =(const Exception& e);
    virtual const char* message() const;
    virtual const char* location() const;
    virtual ~Exception() = 0;
};

編程實(shí)現(xiàn),Exception.cpp

#include "Exception.h"
#include <cstdlib>
#include <cstring>

using namespace std;

namespace DTLib
{
    void Exception::init(const char *message, const char *file, int line)
    {
        m_message = ( message ? strdup(message) : NULL);
        if(NULL != file)
        {
            char sl[16] {0};
            itoa(line, sl, 10);
            m_location = static_cast<char*>(malloc(strlen(sl) +strlen(file) + 2));

            m_location = strcpy(m_location, file);
            m_location = strcat(m_location, ":");
            m_location = strcat(m_location, sl);
        }
        else
        {
            m_location = NULL;
        }
    }

    Exception::Exception(const char *message)
    {
        init(message, NULL, 0);
    }
    Exception::Exception(const char* file, int line)
    {
        init(NULL, file, line);
    }
    Exception::Exception(const char *message, const char *file, int line)
    {
        init(message, file, line);
    }
    Exception::Exception(const Exception& e)
    {
        m_message = strdup(e.m_message);
        m_location = strdup(e.m_location);
    }
    Exception& Exception::operator= (const Exception& e)
    {
        if( this != &e)
        {
            free(m_message);
            free(m_location);

            m_message = strdup(e.m_message);
            m_location = strdup(e.m_location);
        }
        return *this;
    }

    const char* Exception::message() const
    {
        return m_message;
    }
    const char* Exception::location() const
    {
        return m_location;
    }

    Exception::~Exception()
    {
        free(m_location);
        free(m_message);
    }
}

編程實(shí)現(xiàn),Exception.h


#define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__))

class Exception : public Object
{
protected:
    char* m_message;
    char* m_location;
    void init(const char *message, const char *file, int line);
public:
    Exception(const char *message);
    Exception(const char* file, int line);
    Exception(const char *message, const char *file, int line);

    Exception(const Exception& e);
    Exception& operator= (const Exception& e);

    virtual const char* message() const;
    virtual const char* location() const;

    virtual ~Exception() = 0;
};

class ArithmeticException : public Exception
{
public:
    ArithmeticException() : Exception(0){}
    ArithmeticException(const char* message) : Exception(message){}
    ArithmeticException(const char* file, int line) : Exception(file, line){}
    ArithmeticException(const char* message, const char* file, int line) : Exception(message, file, line){}

    ArithmeticException(const ArithmeticException& e) : Exception(e) {}
    ArithmeticException& operator =(const ArithmeticException& e)
    {
        Exception :: operator =(e);
        return *this;
    }
};

class NoEnoughMemoryException : public Exception
{
public:
    NoEnoughMemoryException() : Exception(0){}
    NoEnoughMemoryException(const char* message) : Exception(message){}
    NoEnoughMemoryException(const char* file, int line) : Exception(file, line){}
    NoEnoughMemoryException(const char* message, const char* file, int line) : Exception(message, file, line){}

    NoEnoughMemoryException(const NoEnoughMemoryException& e) : Exception(e) {}
    NoEnoughMemoryException& operator =(const NoEnoughMemoryException& e)
    {
        Exception :: operator =(e);
        return *this;
    }
};

class IndexOutOfBoundsException : public Exception
{
public:
    IndexOutOfBoundsException() : Exception(0){}
    IndexOutOfBoundsException(const char* message) : Exception(message){}
    IndexOutOfBoundsException(const char* file, int line) : Exception(file, line){}
    IndexOutOfBoundsException(const char* message, const char* file, int line) : Exception(message, file, line){}

    IndexOutOfBoundsException(const IndexOutOfBoundsException& e) : Exception(e) {}
    IndexOutOfBoundsException& operator =(const IndexOutOfBoundsException& e)
    {
        Exception :: operator =(e);
        return *this;
    }
};

class NullPointerException : public Exception
{
public:
    NullPointerException() : Exception(0){}
    NullPointerException(const char* message) : Exception(message){}
    NullPointerException(const char* file, int line) : Exception(file, line){}
    NullPointerException(const char* message, const char* file, int line) : Exception(message, file, line){}

    NullPointerException(const NullPointerException& e) : Exception(e) {}
    NullPointerException& operator =(const NullPointerException& e)
    {
        Exception :: operator =(e);
        return *this;
    }
};

class InvaildParemeterException : public Exception
{
public:
    InvaildParemeterException() : Exception(0){}
    InvaildParemeterException(const char* message) : Exception(message){}
    InvaildParemeterException(const char* file, int line) : Exception(file, line){}
    InvaildParemeterException(const char* message, const char* file, int line) : Exception(message, file, line){}

    InvaildParemeterException(const InvaildParemeterException& e) : Exception(e) {}
    InvaildParemeterException& operator =(const InvaildParemeterException& e)
    {
        Exception :: operator =(e);
        return *this;
    }
};

class InvalidOperationException : public Exception
{
public:
    InvalidOperationException() : Exception(0){}
    InvalidOperationException(const char* message) : Exception(message){}
    InvalidOperationException(const char* file, int line) : Exception(file, line){}
    InvalidOperationException(const char* message, const char* file, int line) : Exception(message, file, line){}

    InvalidOperationException(const InvalidOperationException& e) : Exception(e) {}
    InvalidOperationException& operator =(const InvalidOperationException& e)
    {
        Exception :: operator =(e);
        return *this;
    }

};

4.3. .設(shè)計(jì)原則

在構(gòu)建可復(fù)用的庫(kù)時(shí),盡量使用面向?qū)ο蠹夹g(shù)進(jìn)行架構(gòu),盡量使用異常處理機(jī)制分離正常邏輯和異常邏輯。
注意:
1.注意對(duì)于重虛函數(shù)一般不實(shí)現(xiàn),在子類中才會(huì)實(shí)現(xiàn),但是析構(gòu)函數(shù)例外,一但定義,就必須要有實(shí)現(xiàn),否則析構(gòu)過(guò)程會(huì)出錯(cuò)。
2.為什么不直接將message賦值給初始化函數(shù)
原因在于message所指向的字符串可能位于棧、堆、全局?jǐn)?shù)據(jù)區(qū),我們無(wú)法區(qū)控制其生命周期,這樣做不夠安全。

5.頂層父類的創(chuàng)建

5. 1.當(dāng)代軟件架構(gòu)實(shí)踐中的經(jīng)驗(yàn):

  • 盡量使用單純繼承的方式進(jìn)行系統(tǒng)設(shè)計(jì)
  • 盡量保持系統(tǒng)中只存在單一的繼承樹
  • 盡量使用組合關(guān)系代替繼承關(guān)系

    5.2.不幸的事實(shí):

  • C++的語(yǔ)法足夠強(qiáng)大、靈活,使得代碼中可以存在多個(gè)繼承樹
  • C++編譯器的差異使得同樣的代碼可能表現(xiàn)不同的行為(譬如new失敗通常會(huì)返回一個(gè)空指針,但有寫編譯器會(huì)選擇拋出一個(gè)異常)

    5.3.自定義頂層父類

    DTLib::Object的意義:

  • 遵循經(jīng)典的設(shè)計(jì)原則,所有的數(shù)據(jù)結(jié)構(gòu)都繼承自O(shè)bject類
  • 規(guī)范動(dòng)態(tài)類型申請(qǐng)的行為(new失敗返回一個(gè)空指針),提高代碼的可移植性
    接口定義如下:

    class Object
    {
    public:
    void* operator new (unsigned int size) throw();
    void operator delete (void* p);
    void* operator new[] (unsigned int size) throw();
    void operator delete[] (void* p);
    virtual ~Object() = 0;
    };

    編程實(shí)現(xiàn),Object.cpp

    void* Object::operator new(unsigned int size) throw()
    {
        return malloc(size);
    }
    void  Object::operator delete(void *p) throw()
    {
        free(p);
    }
    void* Object::operator new[](unsigned int size) throw()
    {
        return malloc(size);
    }
    void  Object::operator delete[](void *p) throw()
    {
        free(p);
    }
    bool Object::operator == (const Object& obj)
    {
        return this == &obj;
    }
    bool Object::operator != (const Object& obj)
    {
        return this != &obj;
    }
    Object::~Object()
    {
    
    }
    }

    Object.h

    class Object
    {
    public:
    // don't throw any exception,even if alloc fail.
    void* operator new(unsigned int size) throw();
    void  operator delete(void *p) throw();
    void* operator new[](unsigned int size) throw();
    void  operator delete[](void *p) throw();
    bool operator == (const Object& obj);
    bool operator != (const Object& obj);
    virtual ~Object() =0;       // Heavy virtual function(inherited only).
    };

    6.單一繼承樹優(yōu)化

    1.遵循經(jīng)典的設(shè)計(jì)原則,所有的DTLib中的類位于單一的繼承樹
    數(shù)據(jù)結(jié)構(gòu)(02)_模板庫(kù)的基礎(chǔ)設(shè)施搭建
    2.改進(jìn)的關(guān)鍵點(diǎn):

  • Exception類繼承自O(shè)bject類,堆空間創(chuàng)建對(duì)象失敗,返回NULL
  • 新增InvalidOperationException異常類,調(diào)用狀態(tài)不正確時(shí)拋出異常
  • SmartPointer類繼承自O(shè)bject類
    3.DTLib的開發(fā)方式和注意事項(xiàng)
  • 迭×××發(fā),每次完成一個(gè)小目標(biāo)
  • 單一繼承樹
  • 只拋出異常但不處理異常
  • 使用THROW_EXCEPTION拋出異常,提高可移植性(在一些比較老的C++編譯器中是不支持異常處理機(jī)制的,其次有些軟件公司也不允許使用異常處理機(jī)制)。如果將來(lái)用于不支持異常處理的情況時(shí),我們只要將THROW_EXCEPTION這個(gè)宏定義為空即可。
  • 弱耦合性,盡量不使用標(biāo)準(zhǔn)庫(kù)中的類和函數(shù)
    注意:
    1.為什么沒有在init函數(shù)中內(nèi)存申請(qǐng)失敗時(shí)拋出異常,見代碼
    1)從代碼允許邏輯來(lái)講如果此處拋出異常,最終會(huì)生成一個(gè)Exception對(duì)象構(gòu)造時(shí)將再次回到這里
    2)從邏輯分析,如果拋出異常,則應(yīng)該拋出NoEnoughMemoryException這個(gè)子類對(duì)象,父類對(duì)象都沒有生成子類對(duì)象如何產(chǎn)生
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI