• 数据结构开发(1):学习前的准备(上)


    0.目录

    1.泛型编程简介

    2.智能指针示例

    3.异常类构建

    4.小结

    参考前文传送门:
    C++解析(26):函数模板与类模板
    C++解析(20):智能指针与类型转换函数
    C++解析(28):异常处理

    1.泛型编程简介

    数据结构课程的特点:

    • 专注于数据元素之间的关系
    • 专注于特定结构之上的算法

    数据结构课程并不关注数据元素的具体类型!

    如何为数据结构的学习选择合适的语言?
    支持泛型编程的语言最适合数据结构课程的学习?

    泛型编程的概念——不考虑具体数据类型的编程方式

    C++中的函数模板:

    • 一种特殊的函数可用不同类型进行调用
    • 看起来和普通函数很相似,区别是类型可被参数化

    函数模板的语法规则:

    • 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,一个给":",一个给""
         */
        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
    ..StLibmain.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
    ..StLibmain.cpp:11
    

    (剩下的四个子类的实现类似于ArithmeticException类。)

    设计原则:
    在可复用代码库设计时,尽量使用面向对象技术进行架构,尽量使用异常处理机制分离正常逻辑和异常逻辑。

    4.小结

    • 模板是泛型编程理论在C++中的实现
    • 函数模板支持参数的自动推导显示指定
    • 类模板在使用时只能显示指定类型
    • 类模板非常适用于编写数据结构相关的代码
    • 指针特征操作符( -> * )可以被重载
    • 重载指针特征符能够使用对象代替指针
    • 智能指针只能用于指向堆空间中的内存
    • 智能指针的意义在于最大程度的避免内存问题
    • C++中直接支持异常处理的概念
    • try ... catch ... 是C++中异常处理的专用语句
    • try语句处理正常代码逻辑,catch语句处理异常情况
    • 同一个try语句可以跟上多个catch语句
    • 异常处理必须严格匹配,不进行任何的类型转换
    • 现代C++库必然包含充要的异常类族
    • 所有库中的数据结构类都依赖于异常机制
    • 异常机制能够分离库中代码的正常逻辑异常逻辑

  • 相关阅读:
    身份证号码的秘密
    SQL Server中的DATEPART函数的使用
    VS2010+Visual Assist X
    Log4Net使用指南
    JScript中的prototype(原型)属性研究
    使用Transaction访问数据库(C#,TransactionScope,.NET 2.0)
    C# 中奇妙的函数–7. String Split 和 Join
    LinqToSQL实例参见
    得到当前网址的域名 ASP.NET
    oracle中delete、truncate、drop的区别
  • 原文地址:https://www.cnblogs.com/PyLearn/p/10105886.html
Copyright © 2020-2023  润新知