c++模板与类型转换与异常_c++模板类型转换为通用类型-程序员宅基地

技术标签: c++  开发语言  

模板

c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板。凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。 c++提供两种模板机制:函数模板和类模板

  • c++面向对象编程思想:封装、继承、多态
  • c++泛型编程思想:模板

模板的分类:函数模板、类模板
将功能相同,类型不同的函数(类)的类型抽象成虚拟的类型。当调用函数(类实例化对象)的时候,编译器自动将虚拟的类型 具体化。这个就是函数模板(类模板)。

函数模板

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

template<typename T>
函数声明或定义

template — 声明创建模板

typename — 表面其后面的符号是一种数据类型,可以用class代替

T — 通用的数据类型,名称可以替换,通常为大写字母

#include<iostream>
using namespace std;
template<typename T>void fun(T &a,T &b){
    T tem=a;
    a=b;
    b=tem;
}
int main()
{
    int a=10,b=20;
    cout<<a<<" "<<b<<endl;
    fun(a,b);
    cout<<a<<" "<<b<<endl;
    char c='C',d='D';
    fun(c,d);
    cout<<c<<" "<<d<<endl;
}

函数模板 会编译两次:

  • 第一次:是对函数模板 本身编译
  • 第二次:函数调用处 将T的类型具体化

函数模板目标:模板是为了实现泛型,可以减轻编程的工作量,增强函数的重用性。

函数模板的注意点

函数模板 和 普通函数 都识别。(优先选择 普通函数)

函数模板 和 普通函数 都识别。可以强制使用函数模板

//强制使用函数模板
fun<>(a, b);//调用函数模板

函数模板 自动类型推导时 不能对函数的参数 进行 自动类型转换。

#include<iostream>
using namespace std;
template<typename T>void fun(T a,T b){
    T tem=a;
    a=b;
    b=tem;
    cout<<"函数模板"<<endl;
     cout<<a<<"  "<<b<<endl;
}
void fun(int a,int b){
    int tem=a;
    a=b;
    b=tem;
   cout<<"普通函数"<<endl;
    cout<<a<<"  "<<b<<endl;
}
int main()
{
    fun(10,20);    //普通函数
    fun('a','b');   //函数模板
    fun(10,'a');    //普通函数,对a进行了强转为int 97
    fun<int>(10,'b');//函数模板,强制说明T为int类型 就支持自动类型转换
}

函数模板的重载

#include<iostream>
using namespace std;
template<typename T>void fun(T a,T b){
    T tem=a;
    a=b;
    b=tem;
    cout<<"函数模板a,b"<<endl;
     cout<<a<<"  "<<b<<endl;
}
template<typename T>void fun(T a){
     cout<<a<<"函数模板a"<<endl;
}

int main()
{
    fun(12,13);
    fun(12);
}

函数模板的局限性

当函数模板 推导出 T为数组或其他自定义类型数据 可能导致运算符 不识别。

  • 解决办法一:运算符重载
#include<iostream>
using namespace std;
class Da
{
public:
    int data;    //data的属性如果是private,需要在类中声明友元
    Da() {cout<<"无参构造"<<endl;}
    Da(int a){data=a;cout<<"有参构造"<<endl;}
};
ostream& operator <<(ostream &out,Da &ob){
    cout<<ob.data<<endl;
    return out;
}
template<typename T>void fun(T a){cout<<a<<endl;}
int main()
{
    Da ob(100);
    fun(ob);
}

具体化函数模板

#include<iostream>
using namespace std;
class Da
{
public:
    int data;
    Da() {cout<<"无参构造"<<endl;}
    Da(int a){data=a;cout<<"有参构造"<<endl;}
};
/*ostream& operator <<(ostream &out,Da &ob){
    cout<<ob.data<<endl;
    return out;
}*/
template<typename T>void fun(T a){cout<<a<<endl;}    //先有函数模板,调用函数的时候先来这里,
template<>void fun <Da>(Da a){cout<<a.data<<endl;}   //函数模板发现Da类型不知道如何输出,会向下寻找有无函数模板的具体化
int main()
{
    Da ob(1001);
    fun(ob);
}

类模板

类模板和函数模板的定义和使用类似,有两个或多个类,其功能是相同的,仅仅是数据类型不同。 类模板用于实现类所需数据的类型参数化。

#include<iostream>
using namespace std;
template<class T1,class T2>
class Data{
private:
    T1 a;
    T2 b;
public:
    Data(){cout<<"无参构造"<<endl;}
    Data(T1 a,T2 b){
        this->a=a;
        this->b=b;
        cout<<a<<" "<<b<<endl;
    }
};
int main()
{
    Data<int,string> ob(100,"lulu");//实例化对象 不能自动类型推导必须指明T的类型
}

类模板的成员函数在类外实现

#include<iostream>
using namespace std;
//template<class T1,class T2>只修饰class Data他们是一起的
template<class T1,class T2> class Data{  
private:
    T1 a;
    T2 b;
public:
    Data(){cout<<"无参构造"<<endl;}
    Data(T1 a,T2 b);  //类内声明
    void fun(void);   //类内声明
};
/* 
 * template<class T1,class T2>  类模板中成员函数类外实现时,需要加上模板参数列表
 * Data<T1,T2> 才是类的类型
*/
template<class T1,class T2> Data<T1,T2>::Data(T1 a,T2 b){  //类外实现
    this->a=a;
    this->b=b;
    cout<<a<<" "<<b<<endl;
}

template<class T1,class T2> void Data<T1,T2>::fun(){        //类外实现
    cout<<a<<" "<<b<<endl;
}

int main()
{
    Data<int,string> ob(100,"lulu");//实例化对象 不能自动类型推导必须指明T的类型
    //Data<int,string> 才是类的类型
    ob.fun();
}

函数模板作为类模板的友元

#include<iostream>
using namespace std;
//template<class T1,class T2>只修饰class Data他们是一起的
template<class T1,class T2> class Data{
    
    //注意friend 的位置,typename的参数名
   template<typename T3,typename T4> friend void fun2(Data<T3,T4> &ob);
private:
    T1 a;
    T2 b;
public:
    Data(){
    cout<<"无参构造"<<endl;}
    Data(T1 a,T2 b);  //类内声明
    void fun(void);   //类内声明
};
/*
 * template<class T1,class T2>  类模板中成员函数类外实现时,需要加上模板参数列表
 * Data<T1,T2> 才是类的类型
*/
template<class T1,class T2> Data<T1,T2>::Data(T1 a,T2 b){
      //类外实现
    this->a=a;
    this->b=b;
    cout<<a<<" "<<b<<endl;
}

template<class T1,class T2> void Data<T1,T2>::fun(){
            //类外实现
    cout<<a<<" "<<b<<endl;
}


template<typename T3,typename T4> void fun2(Data<T3,T4> &ob){
       //函数模板,形参类型为Data<T3,T4>
    cout<<ob.a<<" "<<ob.b<<endl;
}

int main()
{
    
    Data<int,string> ob(100,"lulu");//实例化对象 不能自动类型推导必须指明T的类型
    //Data<int,string> 才是类的类型
    //ob.fun();
    fun2(ob);
}

普通函数作为类模板的友元

#include<iostream>
using namespace std;
//template<class T1,class T2>只修饰class Data他们是一起的
template<class T1,class T2> class Data{
    
  friend void fun2(Data<int,string> &ob);//声明友元
private:
    T1 a;
    T2 b;
public:
    Data(){
    cout<<"无参构造"<<endl;}
    Data(T1 a,T2 b);  //类内声明
    void fun(void);   //类内声明
};
/*
 * template<class T1,class T2>  类模板中成员函数类外实现时,需要加上模板参数列表
 * Data<T1,T2> 才是类的类型
*/
template<class T1,class T2> Data<T1,T2>::Data(T1 a,T2 b){
      //类外实现
    this->a=a;
    this->b=b;
    cout<<a<<" "<<b<<endl;
}

template<class T1,class T2> void Data<T1,T2>::fun(){
            //类外实现
    cout<<a<<" "<<b<<endl;
}


void fun2(Data<int,string> &ob){
       //函数模板,形参类型为Data<int,string>
    cout<<ob.a<<" "<<ob.b<<endl;
}

int main()
{
    
    Data<int,string> ob(100,"lulu");//实例化对象 不能自动类型推导必须指明T的类型
    //Data<int,string> 才是类的类型
    //ob.fun();
    fun2(ob);
}

模板头文件 和源文件分离问题

data.hpp头文件

#ifndef DATA_H
#define DATA_H
#include<iostream>
using namespace std;
template<class T1, class T2>class Data{
private:
	T1 a;
	T2 b;
public:
	Data();				//无参构造声明
	Data(T1 a, T2 b);  //有参构造声明
	void showData(void);//成员函数申明
};
template<class T1, class T2> Data<T1, T2>::Data(){  //类外实现无参构造
	cout<<"无参构造"<<endl;
}
template<class T1, class T2> Data<T1, T2>::Data(T1 a, T2 b){ //类外实现有参构造
	this->a = a;
	this->b = b;
}
template<class T1, class T2> void Data<T1, T2>::showData(void)   //类外实现成员函数
{
cout<<a<<" "<<b<<endl;
}
#endif // DATA_H

main.cpp

#include <iostream>
#include"data.hpp"   //包含头文件
using namespace std;
int main(int argc, char *argv[])
{
    
Data<int,char> ob1(100,'A'); 
ob1.showData(); //100 A
return 0;
}

数组类模板

.hpp头文件

#ifndef LEISHUZU_HPP
#define LEISHUZU_HPP
#include<iostream>
#include<string.h>
using namespace std;
template<class T> class MyArry{
    template<class T1> friend ostream& operator<<(ostream &out,MyArry<T1> ob);
private:
    T *arr;
    int size;                 //大小
    int capacity;             //容量
public:
    MyArry();                          //无参构造
    MyArry(int capacity);               //有参构造
    MyArry(const MyArry &ob);   //拷贝构造,类中有指针成员且指向堆区空间必须实现拷贝构造完成深拷贝动作。
    ~MyArry();                  //析构,一个类有指针成员,这个类必须写析构函数,释放指针成员所指向空间
    MyArry& operator =(MyArry &ob);   //完成深拷贝,类中有指针成员,且指向堆区空间
    void pushBack(T elem);                  //插入数据
    void sortArray();                       //排序
};
#endif // LEISHUZU_HPP

template<class T>
MyArry<T>::MyArry()   //MyArry<T>才是真正的类型
{
    capacity=5;
    size=0;
    arr=new T[capacity];
    memset(arr,0,sizeof(T)*capacity);  //清空数组内容,需要包含头文件string.h
}

template<class T>
MyArry<T>::MyArry(int capacity)
{
    this->capacity=capacity;
    size=0;
    arr=new T[capacity];
    memset(arr,0,sizeof(T)*capacity);
}

template<class T>
MyArry<T>::MyArry(const MyArry &ob)    //拷贝构造旧对象给新对象赋值完成深拷贝
{
    capacity=ob.capacity;
    size=ob.size;
    arr=new T[capacity];               //开辟新空间
    memset(arr,0,sizeof(T)*capacity);
    memcpy(arr,ob.arr,sizeof(T)*capacity);  //memcpy,完成值的拷贝
}

template<class T>
MyArry<T>::~MyArry()
{
    delete [] arr;       //释放堆区空间
}

template<class T>
MyArry<T> &MyArry<T>::operator =(MyArry &ob)
{
    if(arr!=NULL){delete [] arr;arr=NULL;}//判断this->arr是否存在旧空间,有就删除旧空间
    capacity=ob.capacity;
    size=ob.size;
    arr=new T[capacity];               //开辟新空间
    memset(arr,0,sizeof(T)*capacity);
    memcpy(arr,ob.arr,sizeof(T)*capacity);  //memcpy,完成值的拷贝
    return *this;                          //完成链式操作
}

template<class T>
void MyArry<T>::pushBack(T elem)
{
    if(size==capacity)
    {
        capacity=2*capacity;   //容器满的话就扩展容量
        T *tem=new T[capacity];   //容器满的话就扩展容量,capacity变成了原来的两倍
        if (arr!=NULL)
        {
            memcpy(tem,arr,sizeof(T)*size);   //拷贝旧空间内容
            delete [] arr;         //释放旧空间
        }
    arr=tem;//arr指向新申请的空间
    }
    arr[size]=elem;
    size++;
    return;
}

template<class T>
void MyArry<T>::sortArray()     //冒泡排序
{
    if(size==0)
    {
        cout<<"没有数据"<<endl;
    }
    else
    {
        int i=0,j=0;
        for(i=0;i<size-1;i++)
        {
            for(j=0;j<size-i-1;j++)
            {
                if(arr[j] > arr[j+1])
                {
                    T tmp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1]=tmp;
                }
            }
        }
    }
    return;
}
template<class T1> ostream& operator<<(ostream &out,MyArry<T1> ob)
{
    int i=0;
    for(i=0;i<ob.size;i++)
    {
        out<<ob.arr[i]<<" ";
    }
    out<<endl;
    return out;
}

.cpp源文件

#include <iostream>
#include"leishuzu.hpp"
#include<string>
using namespace std;
class stu
{
    friend ostream& operator <<(ostream &out,stu ob);
private:
    int num;
    string name;
    float score;
public:
    stu(){}
    stu(int num,string name,float score)
    {
        this->num=num;
        this->name=name;
        this->score=score;
    }
    bool operator>(stu ob)      //>符号的重载
    {
        return num>ob.num;
    }
};


ostream& operator <<(ostream &out,stu ob)
{
    out<<ob.num<<" "<<ob.name<<" "<<ob.score<<endl;
    return out;
}

int main(int argc, char *argv[])
{
    MyArry<int>arr1;
    arr1.pushBack(10);
    arr1.pushBack(57);
    arr1.pushBack(34);
    arr1.pushBack(14);
    arr1.pushBack(23);
    arr1.sortArray();
    cout<<arr1<<endl;     //需要重载运算符
    
    MyArry<char>arr2;
    arr2.pushBack('a');
    arr2.pushBack('d');
    arr2.pushBack('c');
    arr2.pushBack('e');
    arr2.pushBack('b');
    arr2.sortArray();
    cout<<arr2<<endl;
    
    MyArry<stu>arr3;
    arr3.pushBack(stu(101,"lulu",98));
    arr3.pushBack(stu(1004,"caicai",9));
    arr3.pushBack(stu(109,"kunkun",97));
    arr3.sortArray();         //需要重载运算符,stu类型两个对象比较,在对应类里实现
    cout<<arr3<<endl;           //需要重载运算符,stu类型两个对象比较,在对应类里实现
     cout << "Hello World!" << endl;
    return 0;
}

类模板 派生出 普通类

#include <iostream>
using namespace std;
template<typename T1,typename T2>
class  stu{
private:
    T1 a;
    T2 b;
public:
    stu(){}
    stu(T1 a,T2 b);
    void fun();
};
template<typename T1, typename T2>
stu<T1,T2>::stu(T1 a, T2 b)      //有参构造
{
    this->a=a;
    this->b=b;
}
template<typename T1, typename T2>
void stu<T1,T2>::fun()          //成员函数
{
 cout<<a<<" "<<b<<endl;
}
class son:public stu<int,char>   //类模板 派生出 普通类
{
public:
    int c;
public:
    son() {}
    son(int a,char b,int c):stu<int,char>(a,b)   //初始化列表,子类实例对象时 必须使用初始化列表 调用成员对象、父类的有参构造。
    {
        this->c=c;
    }
};
int main()
{
    son ob(1,'a',2);
    ob.fun();
    cout<<ob.c<<endl;
}

类模板 派生处 类模板

#include <iostream>
using namespace std;
template<typename T1,typename T2>
class  stu{
    
private:
    T1 a;
    T2 b;
public:
    stu(){
    }
    stu(T1 a,T2 b);
    void fun();
};
template<typename T1, typename T2>
stu<T1,T2>::stu(T1 a, T2 b)      //有参构造
{
    
    this->a=a;
    this->b=b;
}

template<typename T1, typename T2>
void stu<T1,T2>::fun()          //成员函数
{
    
 cout<<a<<" "<<b<<endl;
}
template<typename T1,typename T2,typename T3>
class son:public stu<T1,T2>                  //类模板继承类模板
{
    
public:
    T3 c;
    son(){
    }
    son(T1 a,T2 b,T3 c):stu<T1,T2>(a,b)
    {
    
        this->c=c;
    }
};

int main()
{
    
    son<int,char,int> ob(1,'a',2); //必须指定类型
    ob.fun();
    cout<<ob.c<<endl;
}

类型转换

子类空间肯定是大于等于父类空间的。

子类空间给父类指针保存,子类转换成父类,上行转换(安全)

子类指针转换为基类指针,属于缩小内存访问,所以是安全的。

父类空间给子类指针保存,父类转换成子类,下行转换(不安全,发生内存越界)

父类指针转换为子类指针,由于没有做运行时检查,是不安全的,主要还是子类的访问空间是大于父类,所以多出来的访问空间不保证安全。类似于int转char,int转double等,同样是内存的扩大访问,不能保证安全。

static_cast静态类型转换

普通类型转换

class Base{
    };
class Son:public Base{
    };
class Other{
    };

基本类型

int num = static_cast<int>(3.14);//ok

上行转换:支持 安全

Base *p = static_cast<Base *>(new Son); //子类空间给父类指针保存

下行转换:支持 (不安全)

Son *p2 = static_cast<Son *>(new Base); //父类空间给子类指针保存

不相关类型转换:不支持

Base *p3 = static_cast<Base *>(new Other);//err

dynamic_cast动态类型转换

用于类层次间上行和下行的转换,在进行下行转换时会进行动态类型转换是安全的。

基本类型:不支持

int num = dynamic_cast<int>(3.14);//err

上行转换:支持

对于上行转换,static_cast和dynamic_cast效果一样,都安全;

Base *p1 = dynamic_cast<Base *>(new Son);//ok

不相关类型转换:不支持

Base *p3 = dynamic_cast<Base *>(new Other);//err

对于下行转换:你必须确定要转换的数据确实是目标类型的数据,即需要注意要转换的父类类型指针是否真的指向子类对象,如果是,static_cast和dynamic_cast都能成功;如果不是static_cast能返回,但是不安全,可能会出现访问越界错误,而dynamic_cast在运行时类型检查过程中,判定该过程不能转换,返回NULL。

Son *p2 = dynamic_cast<Son *>(new Base);

const_cast常量转换

将const修饰的指针或引用 转换成 非const (支持)

const int *p1;
int *p2 = const_cast<int *>(p1);

const int &ob = 10;
int &ob1 = const_cast<int &>(ob);

将非const修饰的指针或引用 转换成 const (支持)

int *p3;
const int *p4 = const_cast<const int *>(p3);

int data = 10;    //常量是不能取引用
const int &ob2 = const_cast<const int &>(data);

重新解释转换(reinterpret_cast) (最不安全)

基本类型,不支持

int num=reinterpret_cast<int>(3.14f);//err

基本类型指针,支持

    float *q;
    int *p=reinterpret_cast<int *>(q);

上行转换:支持

Base *p1 = dynamic_cast<Base *>(new Son);//ok

不相关类型转换:支持

Base *p3 = dynamic_cast<Base *>(new Other);

下行转换:支持

Son *p2 = static_cast<Son *>(new Base); //父类空间给子类指针保存


异常

程序遇到错误,然后抛出异常,使用者捕获异常。

异常:是指在程序运行的过程中发生的一些异常事件(如:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足,访问非法内存等等)。(异常是一个类)
c++异常机制相比C语言异常处理的优势?

c语言通过返回值处理异常比如返回0表示成功,-1表示失败。但是函数的返回值可以忽略,异常不可忽略。(忽略异常 程序结束)

整型返回值没有任何语义信息。而异常却包含语义信息,有时你从类名就能够体现出来。

异常的抛出和捕获

try
{
	throw 异常值;
}
catch(异常类型1 异常值1)
{
	处理异常的代码1;
}
catch(异常类型2 异常值2)
{
	处理异常的代码2;
}
catch(...)//任何异常都捕获
{
	处理异常的代码3;
}
try
    {
    
        //throw 1;
        throw 'A';
        //throw 2.14f;
    }
    catch(int e)//捕获,用int类型的普通变量接异常值
    {
    
        cout<<"int异常值为:"<<e<<endl;
    }
    catch(char e)//捕获
    {
    
        cout<<"char异常值为:"<<e<<endl;
    }
    catch(...)//捕获所有异常
    {
    
        cout<<"其他异常值为:err"<<endl;
    }
    return 0;

栈解旋

异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋.

#include <iostream>
using namespace std;
class Data{
public:
    int a;
public:
    Data(){}
    Data(int a)
    {
        this->a = a;
        cout<<"构造函数"<<a<<endl;
    }
    ~Data()
    {
        cout<<"析构函数"<<a<<endl;
    }
};
int main()
{
    try
    {
        Data ob1(10);
        Data ob2(20);
        Data ob3(30);
        throw 1;
    }
    catch(int)//捕获int类型的异常抛出
    {
        cout<<"int异常值为:"<<endl;
    }
    catch(char)//捕获
    {
        cout<<"char异常值为:"<<endl;
    }
    catch(...)//捕获
    {
        cout<<"其他异常值为:"<<endl;
    }
}

异常的接口声明:

描述的是 可以抛出哪些类型的异常

函数默认 可以抛出任何类型的异常(推荐)

void fun01()
{
	//throw 1;
	//throw '1';
	throw "hello";//抛出字符串
}

只能抛出特定类型异常

#include <iostream>
using namespace std;
void fun02() throw(int,char)
{
    
    //throw 1;
    //throw '1';
    throw 3.14f;//抛出 不能捕获,terminate called after throwing an instance of 'float'
}

int main()
{
    
    try
    {
    
        fun02();
    }
    catch(int)//捕获int类型的异常抛出
    {
    
        cout<<"int异常值为:"<<endl;
    }
    catch(char)//捕获
    {
    
        cout<<"char异常值为:"<<endl;
    }
    catch(...)//捕获
    {
    
        cout<<"其他异常值为:"<<endl;
    }
}

不能抛出任何异常

void fun03() throw()
{
    
	throw 1;
	//throw '1';
	//throw "hello";//抛出 不能捕获
}

异常变量的生命周期

以普通对象接异常值,会有拷贝构造,

以对象指针接受异常值,会有空间的开辟

推荐对象引用 接异常值

以普通对象接异常值

#include<iostream>
using namespace std;
class stu{
public:
    stu(){
        cout<<"异常变量构造"<<endl;
    }
    stu(const stu &e){
        cout<<"拷贝构造"<<endl;
    }
    ~stu(){
        cout<<"异常变量析构"<<endl;
    }
};
int main()
{
    try{
        throw   stu();
    }
    catch(stu e)   //会涉及拷贝构造
    {
        cout<<"普通对象接异常"<<endl;
    }
}

结果

异常变量构造
拷贝构造
普通对象接异常
异常变量析构
异常变量析构

以对象指针接受异常值

    try{
        throw  new stu;
    }
    catch(stu *e)
    {
        cout<<"普通对象接异常"<<endl;
        delete e;
    }

结果,会涉及内存空间的开辟和delete

异常变量构造
普通对象接异常
异常变量析构

对象引用 接异常值

    try{
        throw  stu();
    }
    catch(stu &e)
    {
        cout<<"普通对象接异常"<<endl;
    }

既不涉及拷贝构造,又没有空间的开辟

异常的多态

父类的引用捕获子类的异常

#include<iostream>
using namespace std;
//异常基类,用来操作所有的子类,父类使用虚函数。
class BaseException{
public:
    virtual void printError(){}; //虚函数
};
//空指针异常
class NullPointerException : public BaseException{  //父类是BaseException
public:
    virtual void printError()   //子类必须重写虚函数
    {
        cout << "空指针异常!" << endl;
    }
};
//越界异常
class OutOfRangeException : public BaseException{
public:
    virtual void printError()  //子类必须重写虚函数
    {
        cout << "越界异常!" << endl;
    }
};
void doWork()
{
    //throw NullPointerException();
    throw OutOfRangeException();
}
int main()
{
    try{
        doWork();
    }
    catch (BaseException& ex)//父类引用 可以捕获搭配该父类派生出的所有子类的子类
    {
        ex.printError();
    }
}

c++标准异常

c++标准异常

说明:

异常名称 描述
exception 所有标准异常类的父类
bad_alloc 当operator new and operator new[],请求分配内存失败时
bad_exception 这是个特殊的异常,如果函数的异常抛出列表里声明了badexception异常, 当函数内部抛出了异常抛出列表中没有的异常,这是调用的unexpected函数中若抛出异常,不论什么类型,都会被替换为badexception类型
bad_typeid 使用typeid操作符,操作一个NULL指针,而该指针是带有虚函数的类,这时抛出bad_typeid异常
bad_cast 使用dynamic_cast转换引用失败的时候
ios_base::failur io操作过程出现错误
logic_error 逻辑错误,可以在运行前检测的错误
runtime_error 运行时错误,仅在运行时才可以检测的错误

logic_error的子类

异常名称 描述
length_error 试图生成一个超出该类型最大长度的对象时,例如vector的resize操作
domain_error 参数的值域错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数
outofrange 超出有效范围
invalid_argumen 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字 符不是’0’或’1’的时候,抛出该异常

runtime_error的子类

异常名称 描述
range_error 计算结果超出了有意义的值域范围
overflow_error 算术计算上溢
underflow_error 算术计算下溢
invalid_argument 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是’0’或’1’的时候,抛出该异常
    try{
        throw out_of_range("越界了"); //out_of_range 的父类是exception,what是虚函数
    }
    catch (exception &ex)
    {
        cout<<ex.what()<<endl;
    }

编写自己的异常

#include<iostream>
#include<exception> //包含标准异常的头文件
using namespace std;
class NewException:public exception   //必须继承exception
{
    
private:
    string msg;
public:
    NewException(){
    }
    NewException(string msg)
    {
    
        this->msg = msg;
    }
//必须重写父类的what虚函数
virtual const char* what()const throw()//防止父类在子类前抛出标准异常,
{
    
    //将string类转换成char *
    return this->msg.c_str();
}
    ~NewException(){
    }
};
int main()
{
    
    try
    {
    
        throw NewException("自己的异常");
    }
    catch(exception &e)//父类引用接异常值
    {
    
        cout<<e.what()<<endl;
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_55387802/article/details/126689031

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法