数据额结果课程专注于数据元素之间的关系,和数据元素的操作方法,并不关系具体的数据类型,所以选择支持泛型编程的语言最为合适数据结构课程的学习。
不考虑具体的数据类型的编程方式称为泛型编程,举例,对于swap函数考虑下面的泛型写法。
void swap(T a, T b)
{
T t = a;
a = b;
b = t
}
这里的T不是指具体的数据类型,而是泛指任意的数据类型。在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;
}
动态内存申请后,用完后不归还会导致内存泄漏;C++语言中没有垃圾回收机制,指针无法控制所执行的堆空间的生命周期。
使用指针对象代替原生指针,这样在指针生命周期结束时,可以自动调用析构函数,归还对象所使用的堆空间
实现思路:重载指针操作符( *和-> )
一片堆空间只能由一个指针类标识,杜绝指针运算(重载拷贝构造函数、和赋值操作符完成堆空间所有权的转接)
编程实验
#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
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;
}
现代C++库必然包含重要的异常类族
异常类中的接口定义:
#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;
}
};
在构建可复用的库时,尽量使用面向对象技术进行架构,尽量使用异常处理机制分离正常逻辑和异常逻辑。
注意:
1.注意对于重虚函数一般不实现,在子类中才会实现,但是析构函数例外,一但定义,就必须要有实现,否则析构过程会出错。
2.为什么不直接将message赋值给初始化函数
原因在于message所指向的字符串可能位于栈、堆、全局数据区,我们无法区控制其生命周期,这样做不够安全。
DTLib::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).
};
1.遵循经典的设计原则,所有的DTLib中的类位于单一的继承树
2.改进的关键点:
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。