外观
02.泛型编程简介、智能指针示例、异常类构建、顶层父类的创建
约 4879 字大约 16 分钟
数据结构泛型编程智能指针异常类
2022-06-25
八、泛型编程简介
1、数据结构课程的特点
- 专注于数据元素之间的关系
- 专注于特定结构之上的算法
- 数据结构课程并不已关注数据元素的具体类型!
2、问题
如何为数据结构的学习选择合适的语言?
3、经验分享
支持泛型编程的语言最适合数据结构课程的学习?
4、泛型编程的概念
不考虑具体数据类型的编程方式

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

6、函数模板的语法规则
template关键字用于声明开始进行泛型编程
typename关键字用于声明泛指类型

7、函数模板的使用
自动类型推导调用
具体类型显示调用

8、编程实验:函数模板初探
与下一个实验在一起。
9、C++中的类模板
以相同的方式处理不同的类型
在类声明前使用template进行标识
< typename T>用于说明类中使用的泛指类型 T

10、类模板的应用
只能显示指定具体类型,无法自动推导
使用具体类型<Type>定义对象

11、编程实验:类模板初探
#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;
}12、小结
模板是泛型编程理论在C++中的实现
函数模板支持参数的自动推导和显示指定
类模板在使用时只能显示指定类型
类模板非常适用于编写数据结构相关的代码
九、智能指针示例
1、内存泄漏(臭名昭著的Bug )
- 动态申请堆空间,用完后不归还
- C++语言中没有垃圾回收的机制(c#、java、Python有)
- 指针无法控制所指堆空间的生命周期
2、当代C++软件平台中的智能指针
- 指针生命周期结束时主动释放堆空间
- 一片堆空间最多只能由一个指针标识
- 杜绝指针运算和指针比较
3、智能指针的设计方案
通过类模板描述指针的行为
能够定义不同类型的指针对象
重载指针特征操作符(->和*)
利用对象模拟原生指针的行为
4、编程实验:智能指针
SmartPointer.h
#ifndef SMARTPOINTER_H_
#define SMARTPOINTER_H_
/***
* Include Files *
***/
#include "Pointer.h"
/***
* Type Definition *
***/
namespace DTLib
{
template <typename T> // 泛型编程 - 类模板
class SmartPointer : public Pointer<T> // 基于普通指针类
{
public:
SmartPointer(T* p = NULL) : Pointer<T>(p) // 构造函数 - 调用父类构造函数 - 默认指针为NULL
{
}
SmartPointer(const SmartPointer<T>& obj) // 拷贝构造函数
{
this->m_pointer = obj.m_pointer; // 当前对象的堆空间 指向 引入的堆空间
const_cast<SmartPointer<T>&>(obj).m_pointer = NULL; // 将原来的堆空间指针置NULL const_cast 去除对象的const属性
}
SmartPointer<T>& operator= (const SmartPointer<T>& obj) // 重载赋值操作符
{
if( this != &obj ) // 判断是否为 自赋值
{
T* p = this->m_pointer; //为了防止类异常抛出,所以最后释放堆空间
this->m_pointer = obj.m_pointer; // 当前对象的堆空间 指向 引入的堆空间
const_cast<SmartPointer<T>&>(obj).m_pointer = NULL; // 将原来的堆空间指针置NULL const_cast 去除对象的const属性
delete p; // 释放当前指针指向的堆空间
}
return *this; // 返回自己(支持连续赋值)
}
~SmartPointer() // 析构函数 - 释放智能指针对应的堆空间
{
delete this->m_pointer;
}
};
}
#endif // SMARTPOINTER_H_Pointer.h
#ifndef POINTER_H_
#define POINTER_H_
/***
* Include Files *
***/
#include "Object.h"
/***
* Type Definition *
***/
namespace DTLib
{
template < typename T > // 泛型编程 - 类模板 T
class Pointer : public Object
{
protected: // protected 可以被子类直接访问; 不能被外界直接访问
T* m_pointer;
public:
Pointer(T* p = NULL) // 构造函数 - 默认指针为NULL
{
m_pointer = p;
}
T* operator-> () // 重载 -> 返回指针地址
{
return m_pointer;
}
T& operator* () // 重载 * 返回值
{
return *m_pointer;
}
const T* operator-> () const // 重载 -> 返回指针地址 const对象只能调用const的成员函数
{
return m_pointer;
}
const T& operator* () const // 重载 * 返回值 const对象只能调用const的成员函数
{
return *m_pointer;
}
bool isNull() const // 判断当前的智能指针是否为空指针 const对象只能调用const的成员函数
{
return (m_pointer == NULL);
}
T* get() const // 获取智能指针的值 const对象只能调用const的成员函数
{
return m_pointer;
}
virtual ~Pointer() = 0; // 析构函数 纯虚函数(抽象类)
};
template < typename T >
Pointer<T>::~Pointer()
{
}
}
#endif // POINTER_H_5、智能指针分析
智能指针的使用军规:只能用来指向堆空间中的单个对象或者变量
6、小结
指针特征操作符( ->和*)可以被重载
重载指针特征符能够使用对象代替指针
智能指针只能用于指向堆空间中的内存
智能指针的意义在于最大程度的避免内存问题
十、C++异常简介
1、C++内置了异常处理的语法元素try ... catch …
try语句处理正常代码逻辑
catch语句处理异常情况
try语句中的异常由对应的catch语句处理

2、C++通过throw语句抛出异常信息

3、C++异常处理分析
throw抛出的异常必须被catch处理
当前函数能够处理异常,程序继续往下执行
当前函数无法处理异常,则函数停止执行,并返回
未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将停止执行。

4、编程实验:C++异常处理初探
5、C++异常简介
- 同一个try语句可以跟上多个catch语句
- catch语句可以定义具体处理的异常类型
- 不同类型的异常由不同的catch语句负责处理
- try语句中可以抛出任何类型的异常
- catch(.….)用于处理所有类型的异常
- 任何异常都只能被捕获( catch )一次
6、异常处理的匹配规则

7、编程实验:异常类型匹配
#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; // "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;
}8、小结
- C++中直接支持异常处理的概念
- try... catch...是C++中异常处理的专用语句
- try语句处理正常代码逻辑,catch语句处理异常情况
- 同一个try语句可以跟上多个catch语句
- 异常处理必须严格匹配,不进行任何的类型转换
十一、异常类构建
1、异常类构建
- 异常的类型可以是自定义类类型
- 对于类类型异常的匹配依旧是至上而下严格匹配
- 赋值兼容性原则在异常匹配中依然适用
- 一般而言
- 匹配子类异常的catch放在上部
- 匹配父类异常的catch放在下部
2、现代C++库必然包含充要的异常类族
异常类是数据结构类所依赖的“基础设施”!

3、异常类功能定义

4、异常类中的接口定义

5、编程实验:创建异常类族
Exception.cpp
/***
* Include _Files *
***/
#include "Exception.h"
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
/***
* Type Definition *
***/
namespace DTLib
{
/**
* Function: Exception::init()
* Description: 辅助初始化函数
* Table Accessed:
* Table Updated:
* Input: _message 异常的说明信息
* _file 发生异常的文件名
* _line 发生异常的行号
* Output: m_message 异常的说明信息 (类中成员)
* m_location 异常的文件名和行号 (类中成员)
* Return:
* Others: 用于被重载函数调用
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
void Exception::init(const char* _message, const char* _file, int _line)
{
#define STR_CAT_LINE 16 // 行号字符串拼接长度
#define STR_CAT_SYMBOL 2 // 文件名和行号的隔离符号(:); 字符串结束符\0
// 获取 异常的说明信息
m_message = (_message ? strdup(_message) : NULL); //判断_message是否为空(strdup默认入参不为空) strdup将字符串复制到堆空间,返回指针地址
// 获取 发生异常的文件名和行号
if( _file != NULL )
{
// 将行号转为字符串类型
char acLineStrcat[STR_CAT_LINE] = {0};
// itoa(_line, acLineStrcat, 10);
snprintf(acLineStrcat, sizeof(acLineStrcat), "%d", _line);
// 申请堆空间,进行拼接文件名和行号
m_location = static_cast<char*>(malloc(strlen(_file) + strlen(acLineStrcat) + STR_CAT_SYMBOL));
if( m_location != NULL )
{
m_location = strcpy(m_location, _file); // 拷贝文件名
m_location = strcat(m_location, ":"); // 拼接 :
m_location = strcat(m_location, acLineStrcat); // 拼接 行号
}
}
else
{
m_location = NULL;
}
}
/**
* Function: Exception::Exception()
* Description: 重载函数
* Input: _message 异常的说明信息
* _file 发生异常的文件名
* _line 发生异常的行号
* Others: 用于被重载函数调用
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
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);
}
/**
* Function: Exception::Exception()
* Description: 拷贝函数重载(深拷贝)
* Input: Exception 传入待拷贝的Exception类
* Output: Exception 创建的Exception类 (类中成员)
* Return:
* Others: 深拷贝 - 成员指向了动态内存空间
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
Exception::Exception(const Exception& _eWaitCopy)
{
m_message = strdup(_eWaitCopy.m_message);
m_location = strdup(_eWaitCopy.m_location);
}
/**
* Function: Exception::Exception()
* Description: 赋值运算符重载
* Input: Exception 传入待拷贝的Exception类
* Output: Exception 创建的Exception类 (类中成员)
* Return: Exception 当前类的指针
* Others: 返回当前类的指针,代表允许连续赋值
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
Exception& Exception::operator= (const Exception& _eWaitCopy)
{
if( this != &_eWaitCopy )
{
free(m_message);
free(m_location);
m_message = strdup(_eWaitCopy.m_message);
m_location = strdup(_eWaitCopy.m_location);
}
return *this;
}
/**
* Function: Exception::message()
* Description: 返回异常的说明信息的指针
* Input:
* Output:
* Return: char* m_message 返回异常的说明信息的指针
* Others:
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
const char* Exception::message() const
{
return m_message;
}
/**
* Function: Exception::location()
* Description: 返回异常文件名和行号的指针
* Input:
* Output:
* Return: char* m_location 返回异常文件名和行号的指针
* Others:
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
const char* Exception::location() const
{
return m_location;
}
/**
* Function: Exception::~Exception()
* Description: 析构函数
* Input:
* Output:
* Return:
* Others: 纯虚函数(抽象类) - 纯虚函数的实现由子类完成,这里是个例外,但凡定义析构函数就必须要提供实现,不管是不是纯虚的.
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
Exception::~Exception()
{
free(m_message);
free(m_location);
}
}Exception.h
#ifndef EXCEPTION_H_
#define EXCEPTION_H_
/***
* Include Files *
***/
#include "Object.h"
/***
* Type Definition *
***/
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& _eWaitCopy); // 拷贝构造函数
Exception& operator= (const Exception& _eWaitCopy); // 赋值构造函数
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& eWaitCopy) : Exception(eWaitCopy) { }
// 调用父类赋值重载函数
ArithmeticException& operator= (const ArithmeticException& eWaitCopy)
{
Exception::operator=(eWaitCopy);
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& eWaitCopy) : Exception(eWaitCopy) { }
// 调用父类赋值重载函数
NullPointerException& operator= (const NullPointerException& eWaitCopy)
{
Exception::operator=(eWaitCopy);
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& eWaitCopy) : Exception(eWaitCopy) { }
// 调用父类赋值重载函数
IndexOutOfBoundsException& operator= (const IndexOutOfBoundsException& eWaitCopy)
{
Exception::operator=(eWaitCopy);
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& eWaitCopy) : Exception(eWaitCopy) { }
// 调用父类赋值重载函数
NoEnoughMemoryException& operator= (const NoEnoughMemoryException& eWaitCopy)
{
Exception::operator=(eWaitCopy);
return *this;
}
};
/** 参数错误异常子类 **/
class InvalidParameterException : public Exception
{
public:
// 显式调用父类构造函数
InvalidParameterException() : Exception(0) { }
InvalidParameterException(const char* message) : Exception(message) { }
InvalidParameterException(const char* file, int line) : Exception(file, line) { }
InvalidParameterException(const char* message, const char* file, int line) : Exception(message, file, line) { }
// 显式调用父类拷贝构造函数
InvalidParameterException(const InvalidParameterException& eWaitCopy) : Exception(eWaitCopy) { }
// 调用父类赋值重载函数
InvalidParameterException& operator= (const InvalidParameterException& eWaitCopy)
{
Exception::operator=(eWaitCopy);
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& eWaitCopy) : Exception(eWaitCopy) { }
// 调用父类赋值重载函数
InvalidOperationException& operator= (const InvalidOperationException& eWaitCopy)
{
Exception::operator=(eWaitCopy);
return *this;
}
};
}
#endif // EXCEPTION_H_6、设计原则
在可复用代码库设计时,尽量使用面向对象技术进行架构,尽量使用异常处理机制分离正常逻辑和异常逻辑。
7、小结
现代C++库必然包含充要的异常类族
所有库中的数据结构类都依赖于异常机制
异常机制能够分离库中代码的正常逻辑和异常逻辑

十二、顶层父类的创建
1、当代软件架构实践中的经验
- 尽量使用单重继承的方式进行系统设计
- 尽量保持系统中只存在单一的继承树
- 尽量使用组合关系代替继承关系
2、不幸的事实
- C++语言的灵活性使得代码中可以存在多个继承树
- C++编译器的差异使得同样的代码可能表现不同的行为
new操作如果失败会发生什么?
3、创建DTLib::object类的意义
- 遵循经典设计准则,所有数据结构都继承自Object类
- 定义动态内存申请的行为,提高代码的移植性,避免不同编译器的影响
4、顶层父类的接口定义

5、编程实验:顶层父类的创建
Object.h
#ifndef OBJECT_H_
#define OBJECT_H_
/***
* Include _Files *
***/
#include <cstdlib>
/***
* Type Definition *
***/
namespace DTLib
{
class Object
{
public:
void* operator new (size_t size) throw(); // 重载 new
void operator delete (void* p); // 重载 delete
void* operator new[] (size_t size) throw(); // 重载 new[]
void operator delete[] (void* p); // 重载 delete[]
bool operator == (const Object& obj); // 重载 == 自定义类可能会出现比较操作符,此处为默认写法 - 判断地址是否相同
bool operator != (const Object& obj); // 重载 != 自定义类可能会出现比较操作符,此处为默认写法 - 判断地址是否相同
virtual ~Object() = 0; // 析构函数(纯虚函数 抽象类)
};
}
#endif // OBJECT_H_Object.cpp
/***
* Include _Files *
***/
#include "Object.h"
#include <cstdlib>
/***
* Type Definition *
***/
using namespace std;
namespace DTLib
{
/**
* Function: Object::operator new()
* Description: 重载 new
* Table Accessed:
* Table Updated:
* Input: size 字节大小
* Output: void* 指针地址
* Return:
* Others: throw() 这个重载函数不会抛出任何异常
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
void* Object::operator new (size_t size) throw()
{
return malloc(size);
}
/**
* Function: Object::operator delete()
* Description: 重载 delete
* Table Accessed:
* Table Updated:
* Input: void* 指针地址
* Output:
* Return:
* Others:
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
void Object::operator delete (void* p)
{
free(p);
}
/**
* Function: Object::operator new[]()
* Description: 重载 new* Table Accessed:
* Table Updated:
* Input: size 字节大小
* Output: void* 指针地址
* Return:
* Others: throw() 这个重载函数不会抛出任何异常
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
void* Object::operator new[] (size_t size) throw()
{
return malloc(size);
}
/**
* Function: Object::operator delete[]()
* Description: 重载 delete* Table Accessed:
* Table Updated:
* Input: void* 指针地址
* Output:
* Return:
* Others:
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
void Object::operator delete[] (void* p)
{
free(p);
}
/**
* Function: Object::operator ==()
* Description: ==重载函数
* Table Accessed:
* Table Updated:
* Input:
* Output:
* Return:
* Others:
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
bool Object::operator == (const Object& obj)
{
return (this == &obj);
}
/**
* Function: Object::operator !=()
* Description: !=重载函数
* Table Accessed:
* Table Updated:
* Input:
* Output:
* Return:
* Others:
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
bool Object::operator != (const Object& obj)
{
return (this != &obj);
}
/**
* Function: Object::~Object()
* Description: 析构函数
* Table Accessed:
* Table Updated:
* Input:
* Output:
* Return:
* Others:
* Modify Date Version Author Modification
* ------------------------------------------------------------
* 2022/03/09 1.0 Create
**/
Object::~Object()
{
}
}6、小结
- Object类是DTLib中数据结构类的顶层父类
- Object类用于统一动态内存申请的行为
- 在堆中创建Object子类的对象,失败时返回NULL值
- Object类为纯虚父类,所有子类都能进行动态类型识别
十三、类族结构的进化
1、遵循经典设计准则
DTLib 中的所有类位于单一的继承树

2、改进的关键点
- Exception类继承自Object类
- 堆空间中创建异常对象失败时,返回NULL指针
- 新增InvalidOperationException异常类
- 成员函数调用时,如果状态不正确则抛出异常
- SmartPointer 类继承自Object类
- 堆空间中创建智能指针对象失败时,返回NULL指针
3、编程实验:类族结构的进化
Exception.cpp
Exception.h
与上一个相同
4、DTLib 的开发方式和注意事项
- 迭代开发
- 每次完成一个小的目标,持续开发,最终打造可复用类库
- 单—继承树
- 所有类都继承自Object,规范堆对象创建时的行为
- 只抛异常,不处理异常
- 使用THROW_EXCEPTION抛出异常,提高可移植性
- 弱耦合性
- 尽量不使用标准库中的类和函数,提高可移植性
5、第一阶段学习总结
- 数据结构与算法之间的关系
- 算法效率的度量方法
- DTLib的基础设施构建
- 顶层父类
- 智能指针
- 异常类
