温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2020-07-31 14:30:43 来源:网络 阅读:627 作者:三九感冒灵 栏目:编程语言

1.泛型编程简介

1.1.引言

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

1.2.泛型编程的概念

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

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

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

1.3.C++的函模板模板

函数模板是一种特殊的函数,可以使用不同类型进行调用,看起来和普通函数很相似,区别是类型可以被参数化。
语法规则:
数据结构(02)_模板库的基础设施搭建
函数模板的使用有两种方式:
数据结构(02)_模板库的基础设施搭建

1.4.C++类模板

以相同的方式处理不同的类型,在类声明前使用template进行标识。
数据结构(02)_模板库的基础设施搭建
类模板的应用:
只能显示的指定具体的类型,无法自动推导,使用具体类型定义对象:
数据结构(02)_模板库的基础设施搭建
编程实验:

#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.内存泄漏

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

2.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 

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

4.异常类构建

4.1. 标准库异常类族

现代C++库必然包含重要的异常类族
数据结构(02)_模板库的基础设施搭建

4.2. 自定义异常类族

数据结构(02)_模板库的基础设施搭建
异常类中的接口定义:

#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

 #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. .设计原则

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

5.顶层父类的创建

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

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

    5.2.不幸的事实:

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

    5.3.自定义顶层父类

    DTLib::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

    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.单一继承树优化

    1.遵循经典的设计原则,所有的DTLib中的类位于单一的继承树
    数据结构(02)_模板库的基础设施搭建
    2.改进的关键点:

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

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI