數據結構開發(1):學習前的準備(上)

0.目錄

1.泛型編程簡介

2.智能指針示例

3.異常類構建

4.小結

參考前文傳送門:
C++解析(26):函數模板與類模板
C++解析(20):智能指針與類型轉換函數
C++解析(28):異常處理html

1.泛型編程簡介

數據結構課程的特色:ios

  • 專一於數據元素之間的關係
  • 專一於特定結構之上的算法

數據結構課程並不關注數據元素的具體類型!算法

如何爲數據結構的學習選擇合適的語言?
支持泛型編程的語言最適合數據結構課程的學習?編程

泛型編程的概念——不考慮具體數據類型的編程方式
數據結構

C++中的函數模板:架構

  • 一種特殊的函數可用不一樣類型進行調用
  • 看起來和普通函數很類似,區別是類型可被參數化

函數模板的語法規則:ide

  • template關鍵字用於聲明開始進行泛型編程
  • typename關鍵字用於聲明泛型類型

函數模板的使用:函數

  • 自動類型推導調用
  • 具體類型顯示調用

C++中的類模板:學習

  • 以相同的方式處理不一樣的類型
  • 在類聲明前使用template進行標識
  • <typename T>用於說明類中使用的泛指類型 T

類模板的應用:測試

  • 只能顯示指定具體類型,沒法自動推導
  • 使用具體類型<Type>定義對象

(使用QtCreator建立Template項目)
示例——使用函數模板和類模板:

#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;
}

運行結果爲:

a = 1, b = 2
c = 0.02, d = 0.01
5 * 5 = 25
0.3 * 0.3 = 0.09

2.智能指針示例

內存泄漏(臭名昭著的Bug):

  • 動態申請堆空間,用完後不歸還
  • C++語言中沒有垃圾回收機制
  • 指針沒法控制所指堆空間的生命週期

當代C++軟件平臺中的智能指針

  • 指針生命週期結束時主動釋放堆空間
  • 一片堆空間最多隻能由一個指針標識
  • 杜絕指針運算和指針比較

智能指針的設計方案:

  • 經過類模板描述指針的行爲
    1. 可以定義不一樣類型的指針對象
  • 重載指針特徵操做符-> *
    1. 利用對象模擬原生指針的行爲

(使用QtCreator建立StLib項目)
示例——實現智能指針類模板SmartPointer.h:

#ifndef SMARTPOINTER_H
#define SMARTPOINTER_H

namespace StLib {

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
#include <iostream>
#include "SmartPointer.h"

using namespace std;
using namespace StLib;

class Test
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }

    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    SmartPointer<Test> sp = new Test();
    SmartPointer<Test> nsp;

    nsp = sp;

    cout << sp.isNull() << endl;
    cout << nsp.isNull() << endl;

    return 0;
}

運行結果爲:

Test()
1
0
~Test()

智能指針的使用軍規——只能用來指向堆空間中的對象或者變量

3.異常類構建

3.1 C++異常簡介

C++內置了異常處理的語法元素try ... catch ...

  • try語句處理正常代碼邏輯
  • catch語句處理異常狀況
  • try語句中的異常由對應的catch語句處理

C++經過throw語句拋出異常信息:

throw拋出的異常必須被catch處理:

  • 當前函數可以處理異常,程序繼續往下執行
  • 當前函數沒法處理異常,則函數中止執行,並返回

未被處理的異常會順着函數調用棧向上傳播,直到被處理爲止,不然程序將中止執行。

同一個try語句能夠跟上多個catch語句:

  • catch語句能夠定義具體處理的異常類型
  • 不一樣類型的異常由不一樣的catch語句負責處理
  • try語句中能夠拋出任何類型的異常
  • catch(...)用於處理全部類型的異常
  • 任何異常都只能被捕獲(catch)一次

異常處理的匹配規則:

(使用QtCreator建立Exception項目)
示例——異常類型匹配:

#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;   // 產生除 0 異常
    }

    return ret;
}

void Demo1()
{
    try
    {
        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; // "HelloWorld"; // 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(const char* cc)" << endl;
    }
    catch(...)
    {
        cout << "catch(...)" << endl;
    }

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

    return 0;
}

運行結果爲:

main() begin
c = 1
catch(char c)
catch(...)
main() end

3.2 異常類構建

C++中的異常處理:

  • 異常的類型能夠是自定義類類型
  • 對於類類型異常的匹配依舊是至上而下嚴格匹配
  • 賦值兼容性原則在異常匹配中依然適用
  • 通常而言
    1. 匹配子類異常的catch放在上部
    2. 匹配父類異常的catch放在下部

(根據賦值兼容性原則:子類的異常對象能夠被父類的catch語句塊抓住。)

現代C++庫中必然包含充要的異常類族
異常類是數據結構類所依賴的「基礎設施」

異常類功能定義:

異常類中的接口定義:

(在StLib中實現異常類族)
示例1——建立異常類族Exception類:
實現Exception.h

#ifndef EXCEPTION_H
#define EXCEPTION_H

using namespace std;

namespace StLib
{

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

class Exception
{
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();
};

}

#endif // EXCEPTION_H

實現Exception.cpp

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

namespace StLib
{

void Exception::init(const char* message, const char* file, int line)
{
    /* message指向的字符串有可能在棧上,有可能在堆空間,還有可能在全局數據區
     * strdup()將字符串複製一份到堆空間中
     * file:發生異常的文件名
     * line:發生異常的行號
     * m_location的長度加2,一個給":",一個給"\0"
     */
    m_message = strdup(message);

    if( file != NULL )
    {
        char sl[16] = {0};

        itoa(line, sl, 10);

        m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 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_message);
    free(m_location);
}

}

main.cpp測試

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

using namespace std;
using namespace StLib;

int main()
{
    try
    {
        THROW_EXCEPTION(Exception, "test"); // ==>throw Exception("test", __FILE__, __LINE__);
    }
    catch(const Exception& e)
    {
        cout << "catch(const Exception& e)" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }

    return 0;
}

運行結果爲:

catch(const Exception& e)
test
..\StLib\main.cpp:11

(在StLib中實現異常類族)
示例2——實現ArithmeticException.h:

#ifndef EXCEPTION_H
#define EXCEPTION_H

using namespace std;

namespace StLib
{

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

class Exception
{
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;
    }
};

}

#endif // EXCEPTION_H

main.cpp測試

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

using namespace std;
using namespace StLib;

int main()
{
    try
    {
        THROW_EXCEPTION(ArithmeticException, "test");
    }
    catch(const ArithmeticException& e)
    {
        cout << "catch(const ArithmeticException& e)" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    catch(const Exception& e)
    {
        cout << "catch(const Exception& e)" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }

    return 0;
}

運行結果爲:

catch(const ArithmeticException& e)
test
..\StLib\main.cpp:11

(剩下的四個子類的實現相似於ArithmeticException類。)

設計原則:
在可複用代碼庫設計時,儘可能使用面向對象技術進行架構,儘可能使用異常處理機制分離正常邏輯和異常邏輯。

4.小結

  • 模板是泛型編程理論在C++中的實現
  • 函數模板支持參數的自動推導顯示指定
  • 類模板在使用時只能顯示指定類型
  • 類模板很是適用於編寫數據結構相關的代碼
  • 指針特徵操做符( -> * )能夠被重載
  • 重載指針特徵符可以使用對象代替指針
  • 智能指針只能用於指向堆空間中的內存
  • 智能指針的意義在於最大程度的避免內存問題
  • C++中直接支持異常處理的概念
  • try ... catch ... 是C++中異常處理的專用語句
  • try語句處理正常代碼邏輯,catch語句處理異常狀況
  • 同一個try語句能夠跟上多個catch語句
  • 異常處理必須嚴格匹配,不進行任何的類型轉換
  • 現代C++庫必然包含充要的異常類族
  • 全部庫中的數據結構類都依賴於異常機制
  • 異常機制可以分離庫中代碼的正常邏輯異常邏輯

相關文章
相關標籤/搜索