1.MyArray
说明一:赋值构造函数一般与‘=’运算符重载函数同时出现。
说明二:关于左值和右值的介绍
#include<iostream>
using namespace std;
template<class T>
class MyArray{
public:
MyArray<T>(int capacity){
this->mCapacity = capacity;
this->mSize = 0;
//申请内存
this->pAddr = new T[this->mCapacity];
}
MyArray<T>(const MyArray<T>& arr){
this->mSize = arr.mSize;
this->mCapacity = arr.mCapacity;
//申请内存空间
this->pAddr = new T[this->mCapacity];
//数据拷贝
for (int i = 0; i < this->mSize; i++){
this->pAddr[i] = arr.pAddr[i];
}
}
T& operator[](int index){
return this->pAddr[index];
}
MyArray<T>& operator=(const MyArray<T>& arr){
if (this->pAddr != NULL){
delete[] this->pAddr;
}
this->mSize = arr.mSize;
this->mCapacity = arr.mCapacity;
//申请内存空间
this->pAddr = new T[this->mCapacity];
//数据拷贝
for (int i = 0; i < this->mSize; i++){
this->pAddr[i] = arr.pAddr[i];
}
return *this;
}
void PushBack(T& data){
//判断容器中是否有位置
if (this->mSize >= this->mCapacity){
return;
}
//调用拷贝构造 =号操作符
//1. 对象元素必须能够被拷贝
//2. 容器都是值寓意,而非引用寓意 向容器中放入元素,都是放入的元素的拷贝份
//3 如果元素的成员有指针,注意深拷贝和浅拷贝问题
this->pAddr[this->mSize] = data;
//msize++
this->mSize++;
}
// T&& 对右值取引用
void PushBack(T&& data){
//判断容器中是否有位置
if (this->mSize >= this->mCapacity){
return;
}
//调用拷贝构造 =号操作符
//1. 对象元素必须能够被拷贝
//2. 容器都是值寓意,而非引用寓意 向容器中放入元素,都是放入的元素的拷贝份
//3 如果元素的成员有指针,注意深拷贝和浅拷贝问题
this->pAddr[this->mSize] = data;
//msize++
this->mSize++;
}
~MyArray(){
if (this->pAddr != NULL){
delete[] this->pAddr;
}
}
public:
//一共可以容下多少个元素
int mCapacity;
//当前数组有多少元素
int mSize;
//保存数据的首地址
T* pAddr;
};
void test01(){
MyArray<int> marray(20);
int a = 10, b = 20, c = 30, d = 40;
marray.PushBack(a);
marray.PushBack(b);
marray.PushBack(c);
marray.PushBack(d);
//不能对右值取引用
//左值 可以在多行使用
//右值 临时变量 只能当前行使用
marray.PushBack(100);
marray.PushBack(200);
marray.PushBack(300);
for (int i = 0; i < marray.mSize; i++){
cout << marray[i] << " ";
}
cout << endl;
}
class Person{};
void test02(){
Person p1, p2;
MyArray<Person> arr(10);
arr.PushBack(p1);
arr.PushBack(p2);
}
int main(){
test01();
return 0;
}
2.类型转换
a) static_cast
1.可转换基础数据类型(int、char、double等)
char c = static_cast<char>(97);
2.不可转换基础数据类型指针
//下面代码错误
int* p = NULL;
char* sp = static_cast<char*>(p);
3.不可转换对象指针
class Building{};
class Animal{};
class Cat : public Animal{};
//下面代码错误
Building* building = NULL;
Animal* ani = static_cast<Animal*>(building);
4.可转换具有继承关系的对象指针
class Animal{};
class Cat : public Animal{};
//父类指针转成子类指针
Animal* ani = NULL;
Cat* cat = static_cast<Cat*>(ani);
//子类指针转成父类指针
Cat* soncat = NULL;
Animal* anifather = static_cast<Animal*>(soncat);
5.不可将父类对象引用转换为子类对象引用(子类的信息一般比父类多),但可以将子类对象引用转换为父类对象引用。
class Animal{};
class Cat : public Animal{};
//下面代码错误
Animal aniobj;
Animal& aniref = aniobj;
Cat& cat = static_cast<Cat&>(aniref);
//下面代码正确
Cat catobj;
Cat& catref = catobj;
Animal& anifather2 = static_cast<Animal&>(catref);
b) dynamic_cast
1.不可转换基础数据类型
//下面代码错误
int a = 10;
char c = dynamic_cast<char>(a);
2.不可转换没有继承关系的指针
class Building{};
class Animal{};
//下面代码错误
Animal* ani = NULL;
Building* building = dynamic_cast<Building*>(ani);
3.不可将父类对象指针转换为子类对象指针(dynamic_cast会进行类型安全检查)
class Animal{};
class Cat : public Animal{};
//下面代码错误
Animal* ani = NULL;
Cat* cat = dynamic_cast<Cat*>(ani);
4.可以将子类对象指针转换为父类对象指针
class Animal{};
class Cat : public Animal{};
Cat* cat = NULL;
Animal* ani = dynamic_cast<Animal*>(cat);
c)const_cast
1.可以转换基础数据(注意第四行的注释)
int a = 10;
const int& b = a;
//b = 10;
int& c = const_cast<int&>(b); // b本身的const性并没有更改
c = 20;
2.可以转换指针的const属性
const int* p = NULL;
int* p2 = const_cast<int*>(p);
int* p3 = NULL;
const int* p4 = const_cast<const int*>(p3);
d) reinterpret_cast
1.不可转换基础数据类型,代码略
2.无关的指针类型都可转换
class Building{};
class Animal{};
Building* building = NULL;
Animal* ani = reinterpret_cast<Animal*>(building);
3.可转换函数指针类型
typedef void(*FUNC1)(int, int);
typedef int(*FUNC2)(int, char*);
FUNC1 func1;
FUNC2 func2 = reinterpret_cast<FUNC2>(func1);
总结:
static_cast 用于内置的数据类型,还有具有继承关系的指针或者引用;
dynamic_cast 只能转换具有继承关系的指针或者引用,并且只能由子类型转成基类型;
const_cast 用于增加或者去除变量的const性;
reinterpret_cast 可以强制类型转换相互无关的指针类型,包括函数指针都可以进行转换。
3.异常机制
说明一:如果函数B中嵌套函数A,如果抛出异常,则异常是由A抛出(假设A可以抛出异常),举例说明。
#include<iostream>
using namespace std;
int divide(int x ,int y){
if (y == 0)
throw -1; //抛异常
return x / y;
}
void test01(){
//试着去捕获异常
try{
divide(10, 0);
}
catch (int e){ //异常时根据类型进行匹配
cout << "除数为" << e << "!" << endl;
}
}
void CallDivide(int x,int y){
divide(x, y);
}
void test02(){
try{
CallDivide(10,0);
}
catch (int e){
cout << "除数为" << e << endl;
}
}
int main(){
//test01();
test02();
}
在test02()中,CallDivide()会调用divide()。而在divide()函数中,如果除数为0则会抛出异常,这个异常会被test02()中的catch()捕获。这个例子说明调用函数时,会一直追踪至“终点”函数且异常由终点函数抛出。
4.栈解旋
栈的解旋:异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,都会被自动析构,析构的顺序与构造的顺序相反。
#include <iostream>
using namespace std;
class Person{
public:
Person(){
cout << "对象构建!" << endl;
}
~Person(){
cout << "对象析构!" << endl;
}
};
int divide(int x,int y){
Person p1, p2;
if (y == 0)
throw y;
return x / y;
}
int main(){
try{
divide(10,0);
}
catch (int e){
cout << "异常捕获!" << endl;
}
return 0;
}
上述代码的输出为:
对象构建!
对象构建!
对象析构!
对象析构!
异常捕获!
5.异常接口声明
具体语法见下方代码:
#include <iostream>
using namespace std;
//这个函数只能抛出int float char三种类型异常,抛出其他的就报错
void func() throw(int,float,char){
throw "abc";
}
//不能抛出任何异常
void func02() throw(){
throw -1;
}
//可以抛出任何类型异常
void func03(){}
int main(void) {
try{
func();
}
catch (char* str){
cout << str << endl;
}
catch (int e){
cout << "异常!" << endl;
}
catch (...){ //捕获所有异常
cout << "未知类型异常!" << endl;
}
return 0;
}
输出:
abc
6.异常类型
具体用法见下面代码:
#include <iostream>
using namespace std;
void func01(){
throw 1; //抛出int类型异常
}
void func02(){
throw "exception"; //抛出string类型异常
}
class MyException{
public:
MyException(char* str){
error = new char[strlen(str)+1];
strcpy(error, str);
}
MyException(const MyException& ex){ // MyException("我刚写异常!") 赋值给 MyException e
this->error = new char[strlen(ex.error) + 1];
strcpy(this->error,ex.error);
}
MyException& operator=(const MyException& ex){ // 同上
if (this->error != NULL){
delete[] this->error;
this->error = NULL;
}
this->error = new char[strlen(ex.error) + 1];
strcpy(this->error, ex.error);
}
void what(){
cout << error << endl;
}
~MyException(){
if (error != NULL){
delete[] error;
}
}
public:
char* error;
};
void fun03(){
throw MyException("我刚写异常!");
}
void test01(){
try{
func01();
}
catch (int e){
cout << "异常捕获!" << endl;
}
//..................//
try{
func02();
}
catch (char* e){
cout << "异常捕获!" << endl;
}
//..................//
try{
fun03();
}
catch (MyException e){
e.what();
}
}
int main(void){
test01();
return 0;
}
输出:
异常捕获!
异常捕获!
我刚写异常!
7.异常对象生命周期
#include <iostream>
using namespace std;
class MyException{
public:
MyException(){
cout << "构造函数!" << endl;
}
MyException(const MyException& ex){
cout << "拷贝构造!" << endl;
}
~MyException(){
cout << "析构函数!" << endl;
}
};
void func(){
//创建匿名对象 调用构造
//throw new(MyException());
throw MyException();
}
void test01(){
//普通类型元素 引用 指针
//普通元素 异常对象在catch处理完之后就析构
//引用的话 不用调用拷贝构造,异常对象catch处理完之后就析构
//若是指针,异常对象在catch处理之前就析构
try{
func();
}
catch (MyException& e /* MyException* e */){
cout << "异常捕获!" << endl;
/*delete e;*/
}
}
int main(void){
test01();
return 0;
}
说明一:若在test01()中将MyException& e改为MyException e,则当catch()捕获异常时会将e复制构造,然后打印“异常捕获!”,最后两个MyException对象会被析构。
结果如下:
构造函数!
拷贝构造!
异常捕获!
析构函数!
析构函数!
说明二:若test01()中,不更改MyException& e,则在catch()执行完之后,MyException对象会被析构。
结果如下:
构造函数!
异常捕获!
析构函数!
说明三:在test01()中,更改MyException& e为MyException* e,同时将func()中的 throw MyException();该为 throw &(MyException());。则在执行catch()语句之前,MyException对象就会被析构。
结果如下:
构造函数!
析构函数!
异常捕获!
8.Cpp标准异常库举例
注意test02()中的两句注释!!!
#include <iostream>
#include<stdexcept>
using namespace std;
class Person{
public:
Person(){
mAge = 0;
}
void setAge(int age){
if (age < 0 || age > 100){
throw out_of_range("年龄应该在0-100之间!");
}
this->mAge = age;
}
public:
int mAge;
};
void test01(){
Person p;
try{
p.setAge(1000);
}
catch (exception e){ // out_of_range的父类是exception
cout << e.what() << endl;
}
}
class MyOutOfRange : public exception{
public:
MyOutOfRange(char* error){
pError = new char[strlen(error) + 1];
strcpy(pError, error);
}
~MyOutOfRange(){
if (pError != NULL){
delete[] pError;
}
}
virtual const char * what() const{
return pError;
}
public:
char* pError;
};
void fun02(){
throw MyOutOfRange("我自己的out_of_range!");
}
void test02(){
try{
fun02();
}
catch (exception& e){
// 使用引用捕获异常,可以通过基类对象实现虚函数的虚调用,在运行时提现多态性
// 如果使用值传递捕获异常, 则会显示基类what()函数的调用
cout << e.what() << endl;
}
}
int main(void){
//test01();
test02();
return 0;
}
9.继承在异常中的应用
而exception 类位于 头文件中,它被声明为:
class exception{
public:
exception () throw(); //构造函数
exception (const exception&) throw(); //拷贝构造函数
exception& operator= (const exception&) throw(); //运算符重载
virtual ~exception() throw(); //虚析构函数
virtual const char* what() const throw(); //虚函数
}
具体用法参考下面代码:
#include <iostream>
using namespace std;
//异常基类
class BaseMyException{
public:
virtual void what() = 0;
virtual ~BaseMyException(){}
};
class TargetSpaceNullException : public BaseMyException{
public:
virtual void what(){
cout << "目标空间空!" << endl;
}
~TargetSpaceNullException(){}
};
class SourceSpaceNullException : public BaseMyException{
public:
virtual void what(){
cout << "源空间为空!" << endl;
}
~SourceSpaceNullException(){}
};
void copy_str(char* taget,char* source){
if (taget == NULL){
throw TargetSpaceNullException();
}
if (source == NULL){
throw SourceSpaceNullException();
}
//int len = strlen(source) + 1;
while (*source != '\0'){
*taget = *source;
taget++;
source++;
}
}
int main(void){
char* source = "abcdefg";
char buf[1024] = {0};
try{
copy_str(buf, NULL);
}
catch (BaseMyException& ex){
ex.what();
}
}