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

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签