02_泛型编程与STL之黑马程序员day02

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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值