数据结构(02)_模板库的基础设施搭建

8.泛型编程简介

8.1.引言

数据额结果课程专注于数据元素之间的关系,和数据元素的操作方法,并不关系具体的数据类型,所以选择支持泛型编程的语言最为合适数据结构课程的学习。

8.2.泛型编程的概念

不考虑具体的数据类型的编程方式称为泛型编程,举例,对于swap函数考虑下面的泛型写法。

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

这里的T不是指具体的数据类型,而是泛指任意的数据类型。在C++语言中泛型编程通过模板实现。

8.3.C++的函模板模板

函数模板是一种特殊的函数,可以使用不同类型进行调用,看起来和普通函数很相似,区别是类型可以被参数化。
语法规则:

函数模板的使用有两种方式:

8.4.C++类模板

以相同的方式处理不同的类型,在类声明前使用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;
}

9.智能指针

9.1.内存泄漏

动态内存申请后,用完后不归还会导致内存泄漏;C++语言中没有垃圾回收机制,指针无法控制所执行的堆空间的生命周期。

9.2.智能指针

使用指针对象代替原生指针,这样在指针生命周期结束时,可以自动调用析构函数,归还对象所使用的堆空间
实现思路:重载指针操作符( *和-> )
一片堆空间只能由一个指针类标识,杜绝指针运算(重载拷贝构造函数、和赋值操作符完成堆空间所有权的转接)
编程实验

#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

10.C++异常简介

C++中的异常处理:
try处理正常逻辑、throw用于抛出异常、catch用于捕获异常
如果一个异常没有被处理,会沿着函数的调用栈向上传播,直至被处理,或着程序异常终止。
catch捕获异常时会严格匹配,不进行任何形式的转换,catch(…)用与捕获所有异常,放在最后,每一个异常只能被捕获一次
父子兼容原则适用、所以捕获子类的异常在上、父类在下(子类对象可以看做一个父类对象)
编程实验:

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

11.异常类构建

11.1. 标准库异常类族

现代C++库必然包含冲要的异常类族

11.2. 自定义异常类族


异常类中的接口定义:

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

编程实现,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);
    }
}

编程实现,Exception.h

#ifndef EXCEPTION_H
#define EXCEPTION_H

#include "Object.h"

namespace DTLib
{

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

};

}
#endif // EXCEPTION_H

11.3. .设计原则

在构建可复用的库时,尽量使用面向对象技术进行架构,尽量使用异常处理机制分离正常逻辑和异常逻辑。
注意:
1.注意对于重虚函数一般不实现,在子类中才会实现,但是析构函数例外,一但定义,就必须要有实现,否则析构过程会出错。
2.为什么不直接将message赋值给初始化函数
原因在于message所指向的字符串可能位于栈、堆、全局数据区,我们无法区控制其生命周期,这样做不够安全。

12.顶层父类的创建

12. 1.当代软件架构实践中的经验:

—尽量使用单纯继承的方式进行系统设计
—尽量保持系统中只存在单一的继承树
—尽量使用组合关系代替继承关系 

12.2.不幸的事实:

C++的语法足够强大、灵活,使得代码中可以存在多个继承树
C++编译器的差异使得同样的代码可能表现不同的行为(譬如new失败通常会返回一个空指针,但有写编译器会选择抛出一个异常)

12.3.自定义顶层父类

EricLib::Object的意义:
遵循经典的设计原则,所有的数据结构都继承自Object类
规范动态类型申请的行为(new失败返回一个空指针),提高代码的可移植性
接口定义如下:

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

编程实现,Object.cpp

#include "Object.h"
#include <cstdlib>

using namespace std;

namespace DTLib
{
    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

#ifndef OBJECT_H
#define OBJECT_H

namespace DTLib
{
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).
};
}
#endif // OBJECT_H

13.单一继承树优化

1.遵循经典的设计原则,所有的EricLib中的类位于单一的继承树

2.改进的关键点:

  • Exception类继承自Object类,堆空间创建对象失败,返回NULL
  • 新增InvalidOperationException异常类,调用状态不正确时抛出异常
  • SmartPointer类继承自Object类
    3.EricLib的开发方式和注意事项
  • 迭×××发,每次完成一个小目标
  • 单一继承树
  • 只抛出异常但不处理异常
    使用THROW_EXCEPTION抛出异常,提高可移植性(在一些比较老的C++编译器中是不支持异常处理机制的,其次有些软件公司也不允许使用异常处理机制)。如果将来用于不支持异常处理的情况时,我们只要将THROW_EXCEPTION这个宏定义为空即可。
  • 若耦合性,尽量不使用标准库中的类和函数
    编程实验:略
    注意:
    1.为什么没有在init函数中内存申请失败时抛出异常,见代码
    1)从代码允许逻辑来讲如果此处抛出异常,最终会生成一个Exception对象构造时将再次回到这里
    2)从逻辑分析,如果抛出异常,则应该抛出NoEnoughMemoryException这个子类对象,父类对象都没有生成子类对象如何产生

原文地址:http://blog.51cto.com/11134889/2115541

时间: 2024-11-13 08:55:39

数据结构(02)_模板库的基础设施搭建的相关文章

数据结构-02 _用顺序表解决线性表的编程问题

看到这个标题,相必最先应该只到什么是顺序表,什么是线性表. 线性表(linear list):由n(n>=0)个相同的数据类型的数据元素(结点)a0,a1,a2,...an-1 组成的有限序列. 顺序表:把线性表的结构按照逻辑顺序存放在一组地址连续的存储单元里,用这种方式存储的线性表简称顺序表. 线性表的基本操作: 1.初始化操作 2.插入操作:InsertNode(T a,int i) 在线性表的第i个位置插入一个值为a的新元素,使得原序号为i,i+1,...,n 的数据元素的序号变成i+1,

C++标准模板库与数据结构的学习

STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库.它被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中极具革命性的一部分.该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法.为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性. 体验STL中的list STL中提供的list类,是一个双向循环链表类.从每一个元素(节点),都可以访问前面一个元素

C++的标准模板库STL中实现的数据结构之顺序表vector的分析与使用

摘要 本文主要借助对C++的标准模板库STL中实现的数据结构的学习和使用来加深对数据结构的理解.即联系数据结构的理论分析和详细的应用实现(STL),本文是系列总结的第一篇,主要针对线性表中的顺序表(动态数组)STL vector进行分析和总结. 引言 因为前段时间对台大的机器学习基石和技法课程进行了学习,发如今详细的实现中经常涉及到各种类型的数据结构,比方线性表.二叉树.图等,在使用这些数据结构时感到有些吃力.主要是对一些主要的数据结构理解的不够.所以趁着暑假假期.近期一段时间总会抽出时间复习一

模板库(持续更新……)

蒟蒻的模板库 突然发现模板什么的需要存一下了= =因为蒟蒻并没有全部记住…… 一些算法/数据结构的模板就放自己其他博文的链接了,另一些会在这里直接写下来. 以前的我会慢慢整理……今天先写一个→_→(明显还是在偷懒啊喂!能不能靠谱一点啊?) 数学相关: 1 void add(int k,int v){ 2 // printf("add %d %d\n",k,v); 3 F(j,1,tot){ 4 int x=k; 5 while(x){ 6 b[j]+=x/prime[j]*v; 7 x

通过例子进阶学习C++(七)CMake项目通过模板库实现约瑟夫环

本文是通过例子学习C++的第七篇,通过这个例子可以快速入门c++相关的语法. 1.问题描述 回顾一下约瑟夫环问题:n 个人围坐在一个圆桌周围,现在从第 s 个人开始报数,数到第 m 个人,让他出局:然后从出局的下一个人重新开始报数,数到第 m 个人,再让他出局......,如此反复直到所有人全部出局为止. 上一篇我们通过数组.静态链表实现了约瑟夫环,具体参考: 通过例子进阶学习C++(六)你真的能写出约瑟夫环么 本文,我们进一步深入分析约瑟夫环问题,并通过c++模板库实现该问题求解,最后我们说明

标准模板库

-------------------siwuxie095 在长期的编码中,聪明的程序员们发现:有一些代码经常碰到, 而且需求特别稳定,于是,各大公司在出售自己的 IDE 环境时, 就会把这些模板代码打包,一起销售 慢慢地,这些大公司之间就达成了某种共识,觉得应该把这些 涉及模板的通用代码进一步的统一和规范,于是,大家慢慢形 成了一套 C++ 的标准模板,就是现在所看到的标准模板库 标准模板库 标准模板库,即 Standard Template Lib,简称为 STL 标准模板库所涉及的内容非常

C++标准库和标准模板库

C++强大的功能来源于其丰富的类库及库函数资源.C++标准库的内容总共在50个标准头文件中定义. 在C++开发中,要尽可能地利用标准库完成.这样做的直接好处包括: (1)成本:已经作为标准提供,何苦再花费时间.人力重新开发呢: (2)质量:标准库的都是经过严格测试的,正确性有保证: (3)效率:关于人的效率已经体现在成本中了,关于代码的执行效率要相信实现标准库的大牛们的水平: (4)良好的编程风格:采用行业中普遍的做法进行开发. 一.C++标准库 C++标准库的内容分为10类, 分别是:C1.语

STL学习一:标准模板库理论基础

STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称.现然主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间. STL的从广义上讲分为三类:algorithm(算法).container(容器)和iterator(迭代器),容器和算法通过迭代器可以进行无缝 地连接.几乎所有的代码都采 用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会.在C++标准中,STL被组织为下面的13个头文

标准模板库(STL)学习探究之vector容器

标准模板库(STL)学习探究之vector容器  C++ Vectors vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据.为了可以使用vector,必须在你的头文件中包含下面的代码:#include <vector>构造函数. Vectors 包含着一系列连续存储的元素,其行为和数组类