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

智能推荐

攻防世界_难度8_happy_puzzle_攻防世界困难模式攻略图文-程序员宅基地

文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

随便推点

Thinkpad X250 secure boot failed 启动失败问题解决_安装完系统提示secureboot failure-程序员宅基地

文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf

推荐文章

热门文章

相关标签