C++异常

news2024/11/20 13:42:36

21 C++异常

21.1 什么时候会发生异常

  • 1.打开一个不存在的文件

  • 2.请求存储空间失败

  • 3.数组越界等等

21.2 使用abort()函数

  • 1.包含在cstdlib头文件中,包含在std命名空间中

  • 2.当调用abort()函数时,会引发异常并中断程序(Visual Studio 2019);

  • 3.abort()函数是否刷新缓冲区取决于实现,程序员可以调用exit()以刷新缓冲区 相关函数:double hmean(double a, double b);

21.3 程序员手动处理

  • 1.使用指针或引用计算值,如果遇到错误则返回一个错误的计算结果(一般是永远不会用到的值)

  • 2.程序员偏向于使用指针,因为这样可以做出区分,如果是引用的话赋值语句语法一样就没什么区分 相关函数:bool hmean(double a, double b, double* ans);

21.4 异常处理机制

try catch语句:try检查是否会出现异常,catch捕获异常并处理。 首先执行到try,如果try语句块中的语句不引发异常,则直接跳过所有catch语句块;如果try语句块中的语句引发了异常,则检查异常是否与catch语句块括号里的类型一致,如果一致,则执行该catch语句块,如果不一致,则继续检查异常是否与catch语句块括号里的类型一致,以此类推,直到找到一致的catch语句块并执行该catch语句块;如果找不到合适的catch语句块,则使用默认异常处理方法。

本段程序使用的是 throw一个字符串,原则上throw任意数据类型都可,但是一般情况下程序员喜欢throw异常类 相关函数:double hmean1(double a, double b);

21.5 抛出异常类

选择throw异常类的原因:

  • 1.可以使用不同的异常类型去区分不同的异常;

  • 2.异常类可以携带异常信息

  • 3.catch块可以使用异常类携带的异常信息 相关文件:exc_mean.h;相关类:bad_hmean and bad_gmean 相关函数:double hmean2(double a, double b); double gmean(double a, double b);

 

21.6 异常规范(Exception Specifications)

c++11被取消了

格式:throw()部分将出现在原型和函数定义中。

double harm(double a) throw(bad_thing); // may throw bad_thing exception
double marm(double) throw(); // doesn't throw an exception

使用异常规范的原因:

  • 1.异常规范的目的时提醒用户可能需要一个try块。

  • 2.允许编译器添加代码来执行运行时检查,以查看是否违反了异常规范。

为什么被取消?marm()不引发异常,但可能在它调用的函数里面引发异常,也可能现在不引发异常但是将来系统更新后可能引发异常。

在C++程序员一致认为这种异常规范应该取缔。

但是C++11允许一种特别的规范:关键词 noexcept

double marm() noexcept; // marm() doesn't throw an exception

这表明该函数不会抛出异常,程序员认为知道函数不会抛出异常可以帮助编译器优化代码。

如果在运行时,noexecpt函数向外抛出了异常(如果函数内部捕捉了异常并完成处理,这种情况不算抛出异常),程序会直接终止,调用std::terminate()函数,该函数内部会调用std::abort()终止程序。

noexcept()操作符可以报告其操作数是否会引发异常。

21.7 展开堆栈

程序将调用函数指令的地址(返回地址)放在堆栈上,是先进后出的原则。函数参数、函数生成的自动变量、函数调用新函数的信息都被存储在堆栈上;当函数执行完成(return或正常执行完毕)时,其相关的变量、地址都将被弹出,以此类推,最终返回到最初调用函数的地方,每个函数调用都释放掉了占用的内存。 堆栈展开:考虑如果函数由于引发异常终止执行,堆栈的内容会发生什么变化?同样,程序从堆栈中释放内存。但是,程序不会在堆栈上的第一个返回地址处停止,而是继续释放堆栈,直到到达位于try块中的返回地址。

如果在函数调用的函数里面没有处理异常,那么要将异常抛到调用该函数的函数中(throw;)处理,直到处理为止,不然会引发断点。

相关文件:exc_mean.h;相关类:bad_hmean and bad_gmean

相关函数:double hmean2(double a, double b); double gmean(double a, double b); double means(double a, double b);

21.8 try...catch注意事项

  • 1.throw会将异常传递到第一个能够处理该异常的try catch中

  • 2.在throw异常时,编译器总是创建临时copy,原因是异常被抛出后,抛出异常的函数终止,该异常类也就不复存在;为什么catch(参数)中的参数是引用;因为引用可以使用一个基类引用接收继承类的对象,这就允许一个基类引用处理各个继承类的异常了。

class problem {...};
...
void super() throw (problem)
{
     ...
    if (oh_no)
    {
        problem oops;   // construct object
        throw oops;     // throw it
    ...
}
...
try {
    super();
}
catch(problem & p)//此处的p是个引用,而且指向异常的临时对象。
{
// statements
}
  • 3.使用基类引用处理继承类异常时,如果需要一对一处理(每个继承类的处理方式不一样),则应该将最小的孩子(继承类)放在最前面的catch中,而将最基类的放在最后的catch中。

  • 4.当不知道异常的类型时,可以使用默认catch异常。

try {
    duper();
}
catch (...)          // catch whatever is left
{ // statements 
}

21.9 异常类

  • 1.exception类:在头文件exception.h或except.h中;这个类是C++中最基础的类,其他异常类都可以继承exception。 如果不想单独处理每个类,则可以使用exception类的引用catch该异常类。 基类exception有个what()方法,专门用于返回描述异常类的字符串,每次捕获异常是可以手动显示。

  • 2.logic_error类:在头文件stdexcept中,继承exception类 logic_error是以下类的基类:

    • domain_error:引发函数中关于定义域或值域的异常;比如说sin()函数的定义域为(-00,+00),值域为[-1,1],如传递给sin()的值超过定义域,则可以引发域异常

    • invalid_argument:是告诉程序员一个意外的参数传递给了函数。比如只要求传递'0'或'1',如果传递其他字符,则可以引发非法异常。

    • length_error:指示没有足够的空间执行当前操作。比如将一个长度为10的字符串传递给长度为8的字符串,可以引发长度异常。

    • out_of_range:指示索引越界异常

  • 3.runtime_error类:在头文件stdexcept中,继承exception类 runtime_error是以下类的基类:

    • range_error:数据超出指定范围引发range_error异常

    • overflow_error:主要用于整型或浮点类,当计算超过数据类型可表示的最大值时引发overflow_error异常

    • underflow_error:主要用于浮点类型数据,原因是浮点类型数据类型有最小可表示的数据,如果计算超过最小值,则引发underflow_error异常

  • 4.bad_alloc异常类:用于指示在内存分配时可能发生的问题,共有继承自exception类 如果不想抛出bad_alloc异常,则可以使用new(std::nothrow)的方式,使用方法见示例

21.10 异常与继承

见头文件sales.h和实现文件sales.cpp

  • 1.你可以继承一个异常类

  • 2.你可以将异常类嵌套到别的类中

  • 3.嵌套类也可以被继承

21.11 当异常无法控制的时候

主要是针对异常规范来说的,由于它在C++11中已经被取消了,所以可能用处不是很大(了解即可)

21.11.1 意外的异常

意外的异常:就是在异常规范中没有匹配的异常就叫做意外的异常

1.默认unexpected()处理意外的异常:首先调用unexpected()-->调用terminate()-->调用abort().

2.set_unexpected()修改terminate()函数:这两个函数都在头文件exception中,在std命名空间中:

typedef void (*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler f) throw(); // C++98
unexpected_handler set_unexpected(unexpected_handler f) noexcept; // C++11
void unexpected(); // C++98
void unexpected() noexcept; // C+0x

(1)形参:set_unexpected()的参数unexpected_handler是一个函数指针,该指针指向的函数没有形参也没有返回值; ​ (2)作用:使用set_unexpected()后terminate()将会调用set_unexpected()设置的函数而不再使用默认的terminate()函数 ​ (3)注意事项:如果调用set_unexpected()多次,则terminate()采纳最后调用的set_unexpected() ​ (4)举例:myUnexpected()函数作为terminate()调用的函数---不管怎么样都会引发异常导致程序终止(Visual 2019)

3.比set_terminate()更多的规则: unexpected_handler有两种选择: (1)使用默认terminate()终止程序 (2)抛出新异常 选择抛出新异常的结果取决于被unexpected_handler替换的异常和异常规范 (1)如果新抛出的异常与异常规范相匹配,则程序可以正常执行。 (2)如果新抛出的异常与异常规范不匹配,如果异常规范不包含std::bad_exception类型,则调用terminate() (3)如果新抛出的异常与异常规范不匹配,如果异常规范包含std::bad_exception(继承自exception类型并且声明在exception头文件中)类型,则不匹配的异常将会替换为std::bad_exception并处理

21.11.2 未捕获的异常

意外的异常逃过第一层阻碍,后面再无try catch可捕获该异常的即为未捕获的异常 1.默认terminate()处理未捕获异常:未捕获的异常会导致程序的终止,程序终止的过程是:首先调用terminate()-->默认情况下terminate()会调用abort() 2.set_terminate()修改terminate()函数:这两个函数都在头文件exception中,在std命名空间中:

typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler f) throw(); // C++98
terminate_handler set_terminate(terminate_handler f) noexcept; // C++11
void terminate(); // C++98
void terminate() noexcept; // C++11

(1)形参:set_terminate()的参数terminate_handler是一个函数指针,该指针指向的函数没有形参也没有返回值; ​ (2)作用:使用set_terminate()后terminate()将会调用set_terminate()设置的函数而不再使用默认的terminate()函数 ​ (3)注意事项:如果调用set_terminate()多次,则terminate()采纳最后调用的set_terminate() ​ (4)举例:myQuit()函数作为terminate()调用的函数---不管怎么样都会引发异常导致程序终止(Visual 2019)

21.12 关于异常的注意事项

1.异常应该嵌入到程序中,而不是附加到程序中 2.使用异常增加了程序所占的存储空间,减慢了程序的运行,但是依然在一定程度上帮助程序员调试程序,减少错误。 3.将异常应用于模板不会很好因为不同的类型可能引发不同的异常,因此在使用时要小心。 4.将异常应用于动态内存分配也会出现一定的问题,因此在使用时要小心。 5.学习C++语言本身会帮助我们学习异常,学习异常也能帮助我们理解C++本身。

21.13 举例

代码:

main.h

#pragma once
​
#ifndef _MAIN_H
#define _MAIN_H
#include "demo.h"
#include "exc_mean.h"
#include "sales.h"
double hmean(double a, double b);
bool hmean(double a, double b, double* ans);
double hmean1(double a, double b);
double hmean2(double a, double b);
double gmean(double a, double b);
​
// function prototypes
double hmean(double a, double b);
double gmean(double a, double b);
double means(double a, double b);
void myQuit();
void my_func() throw(int);
​
void Argh(int*, int) throw(std::out_of_range, std::bad_exception);
void myUnexpected();
​
double hmean(double a, double b)
{
    if (a == -b)
    {
        std::cout << "untenable arguments to hmean()\n";
        std::abort();
    }
    else
        return 2.0 * a * b / (a + b);
}
​
bool hmean(double a, double b, double* ans)
{
    if (a == -b)
    {
        *ans = DBL_MAX;//返回错误的值
        return false;
    }
    else
    {
        *ans = 2.0 * a * b / (a + b);
        return true;
    }
}
​
double hmean1(double a, double b)
{
    if (a == -b)//这里抛出了一个异常
        throw "bad hmean() arguments: a = -b not allowed";
    return 2.0 * a * b / (a + b);
}
​
double hmean2(double a, double b)
{
    if (a == -b)
        throw bad_hmean(a, b);
    return 2.0 * a * b / (a + b);
}
double gmean(double a, double b)
{
    if (a < 0 || b < 0)
        throw bad_gmean(a, b);
    return std::sqrt(a * b);
}
​
double means(double a, double b)
{
    double am, hm, gm;
    demo d2("found in means()");
    am = (a + b) / 2.0; // arithmetic mean
    try
    {
        hm = hmean2(a, b);
        gm = gmean(a, b);
    }
    catch (bad_hmean& bg) // start of catch block
    {
        bg.mesg();
        std::cout << "Caught in means()\n";
        throw; // rethrows the exception
    }
    d2.show();
    return (am + hm + gm) / 3.0;
}
​
void myQuit()
{
    std::cout << "Terminating due to uncaught exception\n";
    exit(5);
}
​
void my_func() throw(int)
{
    std::cout << "throw 1************************************" << std::endl;
    throw 1;
}
​
void myUnexpected()
{
    throw std::bad_exception(); //or just throw;
}
​
void Argh(int* a, int b) throw(bad_hmean, std::bad_exception)
{
    if (a[0] == b)
    {
        throw bad_hmean(1, -1);
    }
    else
    {
        std::cout << "else***" << std::endl;
        throw 1;
    }
}
​
#endif

demo.h

#pragma once
class demo
{
private:
    std::string word;
public:
    demo(const std::string& str)
    {
        word = str;
        std::cout << "demo " << word << " created\n";
    }
    ~demo()
    {
        std::cout << "demo " << word << " destroyed\n";
    }
    void show() const
    {
        std::cout << "demo " << word << " lives!\n";
    }
};

exc_mean.h

#pragma once
/*
这是定义的两个异常类
*/
#include <iostream>
class bad_hmean
{
private:
    double v1;
    double v2;
public:
    bad_hmean(double a = 0, double b = 0) : v1(a), v2(b) {}
    void mesg();
};
inline void bad_hmean::mesg()
{
    std::cout << "hmean(" << v1 << ", " << v2 << "): "
        << "invalid arguments: a = -b\n";
}
​
class bad_gmean
{
public:
    double v1;
    double v2;
    bad_gmean(double a = 0, double b = 0) : v1(a), v2(b) {}
    const char* mesg();
};
inline const char* bad_gmean::mesg()
{
    return "gmean() arguments should be >= 0\n";
}

sales.h

#pragma once
// sales.h -- exceptions and inheritance
#include <stdexcept>
#include <string>
class Sales
{
public:
    enum { MONTHS = 12 }; // could be a static const
    class bad_index : public std::logic_error
    {
    private:
        int bi; // bad index value
    public:
        explicit bad_index(int ix,
            const std::string& s = "Index error in Sales object\n");
        int bi_val() const { return bi; }
        virtual ~bad_index() throw() {}
    };
    explicit Sales(int yy = 0);
    Sales(int yy, const double* gr, int n);
    virtual ~Sales() { }
    int Year() const { return year; }
    virtual double operator[](int i) const;
    virtual double& operator[](int i);
private:
    double gross[MONTHS];
    int year;
};
class LabeledSales : public Sales
{
public:
    class nbad_index : public Sales::bad_index
    {
    private:
        std::string lbl;
    public:
        nbad_index(const std::string& lb, int ix,
            const std::string& s = "Index error in LabeledSales object\n");
        const std::string& label_val() const { return lbl; }
        virtual ~nbad_index() throw() {}
    };
    explicit LabeledSales(const std::string& lb = "none", int yy = 0);
    LabeledSales(const std::string& lb, int yy, const double* gr, int n);
    virtual ~LabeledSales() { }
    const std::string& Label() const { return label; }
    virtual double operator[](int i) const;
    virtual double& operator[](int i);
private:
    std::string label;
};

main.cpp

/*
Project name :          _17Exceptions
Last modified Date:     2022年3月28日16点31分
Last Version:           V1.0
Descriptions:           异常
什么时候会发生异常:  
    1.打开一个不存在的文件
    2.请求存储空间失败
    3.数组越界等等
*/
#include<iostream>
#include<cstdlib>
#include <new>
#include <exception>
#include<stdexcept>
#include<vector>
#include "main.h"
​
int main()
{
    /*
    使用abort()函数:
        1.包含在cstdlib头文件中,包含在std命名空间中
        2.当调用abort()函数时,会引发异常并中断程序(Visual Studio 2019);
        3.abort()函数是否刷新缓冲区取决于实现,程序员可以调用exit()以刷新缓冲区
    相关函数:double hmean(double a, double b);
    */
    std::cout << "abort()***********************************************************"<<std::endl;
    double x, y, z;
    std::cout << "Enter two numbers: ";
    while (std::cin >> x >> y)
    {
        z = hmean(x, y);
        std::cout << "Harmonic mean of " << x << " and " << y
            << " is " << z << std::endl;
        std::cout << "Enter next set of numbers <q to quit>: ";
    }
    std::cin.clear();//由于输入类型不匹配然后cin被锁了,所以重置cin
    std::cin.get();//读取最后的那个回车符,防止cin后面继续被锁
    /*
    程序员手动处理:
        1.使用指针或引用计算值,如果遇到错误则返回一个错误的计算结果(一般是永远不会用到的值)
        2.程序员偏向于使用指针,因为这样可以做出区分,如果是引用的话赋值语句语法一样就没什么区分
        相关函数:bool hmean(double a, double b, double* ans);
    */
    std::cout << "程序员手动处理****************************************************" << std::endl;
    std::cout << "Enter two numbers: ";
    while (std::cin >> x >> y)
    {
        if (hmean(x, y, &z))
            std::cout << "Harmonic mean of " << x << " and " << y
            << " is " << z << std::endl;
        else
            std::cout << "One value should not be the negative "
            << "of the other - try again.\n";
        std::cout << "Enter next set of numbers <q to quit>: ";
    }
    /*
    * 异常处理机制:
    try catch语句:try检查是否会出现异常,catch捕获异常并处理
    首先执行到try,如果try语句块中的语句不引发异常,则直接跳过所有catch语句块;如果try语句块中的语句引发了异常,
    则检查异常是否与catch语句块括号里的类型一致,如果一致,则执行该catch语句块,如果不一致,则继续检查异常
    是否与catch语句块括号里的类型一致,以此类推,直到找到一致的catch语句块并执行该catch语句块;如果找不到合适
    的catch语句块,则使用默认异常处理方法。
​
    本段程序使用的是 throw一个字符串,原则上throw任意数据类型都可,但是一般情况下程序员喜欢throw异常类
    相关函数:double hmean1(double a, double b);
    */
    std::cout << "try catch************************************************************" << std::endl;
    std::cin.clear();//由于输入类型不匹配然后cin被锁了,所以重置cin
    std::cin.get();//读取最后的那个回车符,防止cin后面继续被锁
    std::cout << "Enter two numbers: ";
    while (std::cin >> x >> y)
    {
        try { // start of try block
            z = hmean1(x, y);
        } // end of try block
        catch (const char* s) // start of exception handler
        {
            std::cout << s << std::endl;
            std::cout << "Enter a new pair of numbers: ";
            continue;
        } // end of handler
        std::cout << "Harmonic mean of " << x << " and " << y
            << " is " << z << std::endl;
        std::cout << "Enter next set of numbers <q to quit>: ";
    }
    /*
    抛出异常类:
        选择throw异常类的原因:
            1.可以使用不同的异常类型去区分不同的异常;
            2.异常类可以携带异常信息
            3.catch块可以使用异常类携带的异常信息
        相关文件:exc_mean.h;相关类:bad_hmean and bad_gmean
        相关函数:double hmean2(double a, double b);
                  double gmean(double a, double b);
    */
    std::cout << "抛出异常类*********************************************************" << std::endl;
    std::cin.clear();//由于输入类型不匹配然后cin被锁了,所以重置cin
    std::cin.get();//读取最后的那个回车符,防止cin后面继续被锁
    std::cout << "Enter two numbers: ";
    while (std::cin >> x >> y)
    {
        try { // start of try block
            z = hmean2(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y
                << " is " << z << std::endl;
            std::cout << "Geometric mean of " << x << " and " << y
                << " is " << gmean(x, y) << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }// end of try block
        catch (bad_hmean& bg) // start of catch block
        {
            bg.mesg();
            std::cout << "Try again.\n";
            continue;
        }
        catch (bad_gmean& hg)
        {
            std::cout << hg.mesg();
            std::cout << "Values used: " << hg.v1 << ", "
                << hg.v2 << std::endl;
            std::cout << "Sorry, you don't get to play any more.\n";
            break;
        } // end of catch block
    }
    /*
    异常规范(Exception Specifications):c++11被取消了
    格式:double harm(double a) throw(bad_thing); // may throw bad_thing exception
          double marm(double) throw(); // doesn't throw an exception
    为什么被取消?harm()可能会引发bad_thing异常,也可能是在它调用的函数里面引发的异常,也可能现在不引发异常但是将来系统更新后可能引发异常。
    在C++程序员俱乐部大家一致认为这种异常规范应该取缔。
​
    但是C++11允许一种特别的规范:关键词 noexcept
    double marm() noexcept; // marm() doesn't throw an exception
    这表明该函数不会抛出异常。
    
    noexcept()操作符可以报告其操作数是否会引发异常。
    */
    /*
    Unwinding the Stack(展开堆栈):
    程序将调用函数指令的地址(返回地址)放在堆栈上,是先进后出的原则。函数参数、函数生成的自动变量、函数调用新函数的信息都被存储在堆栈上;
    当函数执行完成(return或正常执行完毕)时,其相关的变量、地址都将被弹出,以此类推,最终返回到最初调用函数的地方,每个函数调用都释放掉了占用的内存。
    
    考虑如果函数由于引发异常终止执行,堆栈的内容会发生什么变化,堆栈上关于try与throw之间生成的所有参数、函数地址、自动变量等等都会被弹出;
    这就叫做展开堆栈(Unwinding the Stack);这个机制是为了保证try与throw之间占用的内存被正常释放。
​
    如果在函数调用的函数里面没有处理异常,那么要将异常抛到调用该函数的函数中(throw;)处理,直到处理为止,不然会引发断点。
    相关文件:exc_mean.h;相关类:bad_hmean and bad_gmean
    相关函数:double hmean2(double a, double b);
              double gmean(double a, double b);
              double means(double a, double b);
    */
    /*
    注意事项:
        1.throw会将异常传递到第一个能够处理该异常的try catch中
        2.在throw异常时,编译器总是创建临时copy,原因是catch(参数)中的参数是引用;
        但是为什么要使用引用而不用对象呢?因为引用可以使用一个基类引用接收继承类的对象,这就允许一个基类引用处理各个继承类的异常了。
        3.使用基类引用处理继承类异常时,如果需要一对一处理(每个继承类的处理方式不一样),则应该将最小的孩子(继承类)放在最前面的catch中,而将最基类的放在最后的catch中。
    */
    std::cout << "Unwinding the Stack******************************************************" << std::endl;
    std::cin.clear();//由于输入类型不匹配然后cin被锁了,所以重置cin
    std::cin.get();//读取最后的那个回车符,防止cin后面继续被锁
    demo d1("found in block in main()");
    std::cout << "Enter two numbers: ";
    while (std::cin >> x >> y)
    {
        try { // start of try block
            z = means(x, y);
            std::cout << "The mean mean of " << x << " and " << y
                << " is " << z << std::endl;
            std::cout << "Enter next pair: ";
        } // end of try block
        catch (bad_hmean& bg) // start of catch block
        {
            bg.mesg();
            std::cout << "Try again.\n";
            continue;
        }
        catch (bad_gmean& hg)
        {
            std::cout << hg.mesg();
            std::cout << "Values used: " << hg.v1 << ", "
                << hg.v2 << std::endl;
            std::cout << "Sorry, you don't get to play any more.\n";
            break;
        } // end of catch block
    }
    d1.show();
    /*
    异常类:
        1.exception类:在头文件exception.h或except.h中;这个类是C++中最基础的类,其他异常类都可以继承exception。
            如果不想单独处理每个类,则可以使用exception类的引用catch该异常类。
            基类exception有个what()方法,专门用于返回描述异常类的字符串,每次捕获异常是可以手动显示。
        2.logic_error类:在头文件stdexcept中,继承exception类
        logic_error是以下类的基类:
                domain_error:引发函数中关于定义域或值域的异常;比如说sin()函数的定义域为(-00,+00),值域为[-1,1],如传递给sin()的值超过定义域,则可以引发域异常
                invalid_argument:是告诉程序员一个意外的参数传递给了函数。比如只要求传递'0'或'1',如果传递其他字符,则可以引发非法异常。
                length_error:指示没有足够的空间执行当前操作。比如将一个长度为10的字符串传递给长度为8的字符串,可以引发长度异常。
                out_of_range:指示索引越界异常
        3.runtime_error类:在头文件stdexcept中,继承exception类
        runtime_error是以下类的基类:
            range_error:数据超出指定范围引发range_error异常
            overflow_error:主要用于整型或浮点类,当计算超过数据类型可表示的最大值时引发overflow_error异常
            underflow_error:主要用于浮点类型数据,原因是浮点类型数据类型有最小可表示的数据,如果计算超过最小值,则引发underflow_error异常
        4.bad_alloc异常类:用于指示在内存分配时可能发生的问题,共有继承自exception类
            如果不想抛出bad_alloc异常,则可以使用new(std::nothrow)的方式,使用方法见示例
    */
    std::cout << "异常类*********************************************************" << std::endl;
    std::cout << "throw bad_alloc************************************************" << std::endl;
    struct Big
    {
        double stuff[20000];
    };
    Big* pb;
    try {
        std::cout << "Trying to get a big block of memory:\n";
        pb = new Big[10000]; // 1,600,000,000 bytes
        std::cout << "Got past the new request:\n";
    }
    catch (std::bad_alloc& ba)
    {
        std::cout << "Caught the exception!\n";
        std::cout << ba.what() << std::endl;
        exit(EXIT_FAILURE);
    }
    std::cout << "Memory successfully allocated\n";
    pb[0].stuff[0] = 4;
    std::cout << pb[0].stuff[0] << std::endl;
    delete[] pb;
    std::cout << "new(std::nothrow)************************************************" << std::endl;
    pb = new(std::nothrow) Big[10000]; // 1,600,000,000 bytes
    if (pb == 0)
    {
        std::cout << "Could not allocate memory. Bye.\n";
        exit(EXIT_FAILURE);
    }
    /*
    异常与继承:见头文件sales.h和实现文件sales.cpp
        1.你可以继承一个异常类
        2.你可以将异常类嵌套到别的类中
        3.嵌套类也可以被继承
    */
    std::cout << "异常与继承*********************************************************" << std::endl;
    double vals1[12] =
    {
    1220, 1100, 1122, 2212, 1232, 2334,
    2884, 2393, 3302, 2922, 3002, 3544
    };
    double vals2[12] =
    {
    12, 11, 22, 21, 32, 34,
    28, 29, 33, 29, 32, 35
    };
    Sales sales1(2011, vals1, 12);
    LabeledSales sales2("Blogstar", 2012, vals2, 12);
    std::cout << "First try block:\n";
    try
    {
        int i;
        std::cout << "Year = " << sales1.Year() << std::endl;
        for (i = 0; i < 12; ++i)
        {
            std::cout << sales1[i] << ' ';
            if (i % 6 == 5)
                std::cout << std::endl;
        }
        std::cout << "Year = " << sales2.Year() << std::endl;
        std::cout << "Label = " << sales2.Label() << std::endl;
        for (i = 0; i <= 12; ++i)
        {
            std::cout << sales2[i] << ' ';
            if (i % 6 == 5)
                std::cout << std::endl;
        }
        std::cout << "End of try block 1.\n";
    }
    catch (LabeledSales::nbad_index& bad)
    {
        std::cout << bad.what();
        std::cout << "Company: " << bad.label_val() << std::endl;
        std::cout << "bad index: " << bad.bi_val() << std::endl;
    }
    catch (Sales::bad_index& bad)
    {
        std::cout << bad.what();
        std::cout << "bad index: " << bad.bi_val() << std::endl;
    }
    std::cout << "\nNext try block:\n";
    try
    {
        sales2[2] = 37.5;
        sales1[20] = 23345;
        std::cout << "End of try block 2.\n";
    }
    catch (LabeledSales::nbad_index& bad)
    {
        std::cout << bad.what();
        std::cout << "Company: " << bad.label_val() << std::endl;
        std::cout << "bad index: " << bad.bi_val() << std::endl;
    }
    catch (Sales::bad_index& bad)
    {
        std::cout << bad.what();
        std::cout << "bad index: " << bad.bi_val() << std::endl;
    }
    /*
    当异常无法控制的时候:主要是针对异常规范来说的,由于它在C++11中已经被取消了,所以可能用处不是很大(了解即可)
        意外的异常:就是在异常规范中没有匹配的异常就叫做意外的异常
            1.默认unexpected()处理意外的异常:首先调用unexpected()-->调用terminate()-->调用abort().
            2.set_unexpected()修改terminate()函数:这两个函数都在头文件exception中,在std命名空间中:
                typedef void (*unexpected_handler)();
                unexpected_handler set_unexpected(unexpected_handler f) throw(); // C++98
                unexpected_handler set_unexpected(unexpected_handler f) noexcept; // C++11
                void unexpected(); // C++98
                void unexpected() noexcept; // C+0x
                (1)形参:set_unexpected()的参数unexpected_handler是一个函数指针,该指针指向的函数没有形参也没有返回值;
                (2)作用:使用set_unexpected()后terminate()将会调用set_unexpected()设置的函数而不再使用默认的terminate()函数
                (3)注意事项:如果调用set_unexpected()多次,则terminate()采纳最后调用的set_unexpected()
                (4)举例:myUnexpected()函数作为terminate()调用的函数---不管怎么样都会引发异常导致程序终止(Visual 2019)
            3.比set_terminate()更多的规则:
                unexpected_handler有两种选择:
                (1)使用默认terminate()终止程序
                (2)抛出新异常
                选择抛出新异常的结果取决于被unexpected_handler替换的异常和异常规范
                (1)如果新抛出的异常与异常规范相匹配,则程序可以正常执行。
                (2)如果新抛出的异常与异常规范不匹配,如果异常规范不包含std::bad_exception类型,则调用terminate()
                (3)如果新抛出的异常与异常规范不匹配,如果异常规范包含std::bad_exception(继承自exception类型并且声明在exception头文件中)类型,则不匹配的异常将会替换为std::bad_exception并处理
        未捕获的异常:意外的异常逃过第一层阻碍,后面再无try catch可捕获该异常的即为未捕获的异常
            1.默认terminate()处理未捕获异常:未捕获的异常会导致程序的终止,程序终止的过程是:首先调用terminate()-->默认情况下terminate()会调用abort()
            2.set_terminate()修改terminate()函数:这两个函数都在头文件exception中,在std命名空间中:
                typedef void (*terminate_handler)();
                terminate_handler set_terminate(terminate_handler f) throw(); // C++98
                terminate_handler set_terminate(terminate_handler f) noexcept; // C++11
                void terminate(); // C++98
                void terminate() noexcept; // C++11
                (1)形参:set_terminate()的参数terminate_handler是一个函数指针,该指针指向的函数没有形参也没有返回值;
                (2)作用:使用set_terminate()后terminate()将会调用set_terminate()设置的函数而不再使用默认的terminate()函数
                (3)注意事项:如果调用set_terminate()多次,则terminate()采纳最后调用的set_terminate()
                (4)举例:myQuit()函数作为terminate()调用的函数---不管怎么样都会引发异常导致程序终止(Visual 2019)
    */
    //对于下面的语句,我的系统就是会引发“0x765EB922 处(位于 _17Exceptions.exe 中)有未经处理的异常: Microsoft C++ 异常: int,位于内存位置 0x00FDF42C 处。”异常,我也不知道为什么
    /*std::set_terminate(myQuit);
    my_func();*/
    //对于下面的语句,我的系统也引发了“0x765EB922 处(位于 _17Exceptions.exe 中)有未经处理的异常: Microsoft C++ 异常: int,位于内存位置 0x0038F490 处。”异常,我也不知道什么
    /*set_unexpected(myUnexpected);
    try {
        int a[2] = { 1,2 };
        Argh(a, 2);
    }
    catch (bad_hmean& ex)
    {
        std::cout << "bad_hmean*****************************" << std::endl;
    }
    catch (std::bad_exception& ex)
    {
        std::cout << "bad_exception*****************************" << std::endl;
    }*/
​
    /*
    关于异常的注意事项:
        1.异常应该嵌入到程序中,而不是附加到程序中
        2.使用异常增加了程序所占的存储空间,减慢了程序的运行,但是依然在一定程度上帮助程序员调试程序,减少错误。
        3.将异常应用于模板不会很好因为不同的类型可能引发不同的异常,因此在使用时要小心。
        4.将异常应用于动态内存分配也会出现一定的问题,因此在使用时要小心。
        5.学习C++语言本身会帮助我们学习异常,学习异常也能帮助我们理解C++本身。
    */
    return 0;
}

运行结果:

abort()***********************************************************
Enter two numbers: 99 88
Harmonic mean of 99 and 88 is 93.1765
Enter next set of numbers <q to quit>: q
程序员手动处理****************************************************
Enter two numbers: 99 88
Harmonic mean of 99 and 88 is 93.1765
Enter next set of numbers <q to quit>: q
try catch************************************************************
Enter two numbers: 99 88
Harmonic mean of 99 and 88 is 93.1765
Enter next set of numbers <q to quit>: q
抛出异常类*********************************************************
Enter two numbers: 99 88
Harmonic mean of 99 and 88 is 93.1765
Geometric mean of 99 and 88 is 93.3381
Enter next set of numbers <q to quit>: q
Unwinding the Stack******************************************************
demo found in block in main() created
Enter two numbers: 99 88
demo found in means() created
demo found in means() lives!
demo found in means() destroyed
The mean mean of 99 and 88 is 93.3382
Enter next pair: q
demo found in block in main() lives!
异常类*********************************************************
throw bad_alloc************************************************
Trying to get a big block of memory:
Caught the exception!
bad allocation
​
D:\Prj\_C++Self\_17Exceptions\Debug\_17Exceptions.exe (进程 8964)已退出,代码为 1。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/29126.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【PlasticSCM Could Edition】新版本云托管浅试 (与踩一些坑)

【PlasticSCM Could Edition】新版本云托管浅试前言尝试对比前言 UnityHub 3.3.0-c7 中&#xff0c;下载了一个 2021LST 的版本&#xff0c;毕竟为了做毕设&#xff0c;用 2020LST 的感觉老了点… 然后新建了一个项目&#xff0c;勾选使用 版本控制 &#xff0c;但是报错 那先…

【附源码】计算机毕业设计JAVA药品管理系统

【附源码】计算机毕业设计JAVA药品管理系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA mybati…

STM32CubeMX学习笔记(48)——USB接口使用(MSC基于外部Flash模拟U盘)

一、USB简介 USB&#xff08;Universal Serial BUS&#xff09;通用串行总线&#xff0c;是一个外部总线标准&#xff0c;用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、…

HTTP —— HTTP 响应详解, 构造 HTTP 请求

JavaEE传送门JavaEE 【前端】JavaScript —— WebAPI HTTP —— HTTP 协议中的细节(超详细!!) 目录HTTP 响应详解认识 "状态码"状态码小结认识响应 "报头" headerContent-Type认识响应 "正文" body构造 HTTP 请求form 表单ajaxpostmanHTTP 响应…

API接口怎么使用(教你使用api接口获取数据)

API是应用程序的开发接口&#xff0c;在开发程序的时候&#xff0c;我们有些功能可能不需要从到到位去研发&#xff0c;我们可以拿现有的开发出来的功能模块来使用&#xff0c;而这个功能模块&#xff0c;就叫做库(libary)。比如说&#xff1a;要实现数据传输的安全&#xff0c…

【Linux】常见指令汇总

每个优秀的人&#xff0c;都有一段沉默的时光。 那段时光&#xff0c;是付出了很多努力&#xff0c;却得不到结果的日子&#xff0c;我们把它叫做扎根。 文章目录一、xshell的介绍操作系统1.1 xshell1.2 操作系统二、Linux下基本指令2.1 ls(显示当前目录下的文件列表&#xff…

【IVIF:引入注意力机制:GAN:双鉴别器】

AttentionFGAN: Infrared and Visible Image Fusion Using Attention-Based Generative Adversarial Networks &#xff08; AttentionFGAN&#xff1a;使用基于注意的生成对抗网络进行红外和可见光图像融合&#xff09; 在本论文中&#xff0c;我们将多尺度注意机制集成到GAN…

redis优化与一些问题

文章目录优化采用SSD硬盘-提升磁盘读写的速度控制 redis 的内存在10G以内&#xff0c;防止fork耗时太长fork 注意事项设置内存淘汰策略vm.overcommit_memory1尽可能地使用 hash 哈希存储参数调优swapinessulimitTCP backlog客户端缓冲优化碎片优化问题缓存与数据库数据不一致将…

换个地方写helloworld

1、Linux中I/O讲解 今天&#xff0c;我们重新学习C语言的基础&#xff0c;特别是I/O口的讲解。 所谓标准 I/O 库则是标准 C 库中用于文件 I/O 操作&#xff08;譬如读文件、写文件等&#xff09;相关的一系列库函数的集合&#xff0c;通常标准 I/O 库函数相关的函数定义都在头…

实时输出Java8 HashMap数据结构

看过 Java 8 HashMap 源码的知道底层数据结构是数组、链表和红黑树实现的&#xff0c;从Debug调试或者序列化输出的都是K,V值&#xff0c;没法直观的看到上述的数据结构&#xff0c;为了直观的看到数据结构存储方面的变化&#xff0c;本文通过动图演示HashMap的结构变化。 为了…

【6 - 完结】Sql Server - 郝斌(identity、视图、事务、索引、存储过程、触发器、游标、TL_SQL)

课程地址&#xff1a;数据库 SQLServer 视频教程全集&#xff08;99P&#xff09;| 22 小时从入门到精通_哔哩哔哩_bilibili ​ 目录 identity&#xff08;主键自动增长&#xff0c;用户不需要为identity修饰的主键赋值&#xff09; 用法 如何重新设置identity字段的值 如…

从React源码来学hooks是不是更香呢

本文将讲解 hooks 的执行过程以及常用的 hooks 的源码。 hooks 相关数据结构 要理解 hooks 的执行过程&#xff0c;首先想要大家对 hooks 相关的数据结构有所了解&#xff0c;便于后面大家顺畅地阅读代码。 Hook 每一个 hooks 方法都会生成一个类型为 Hook 的对象&#xff…

wpf布局学习二 wpf xaml 与android xml界面对比, C++图片旋转与缩放好复杂放弃

弄不明白的事&#xff0c;还是不要去做。 没懂清楚原理&#xff0c;不要尝试去修改。浪费时间。 wpf布局学习二 <Window x:Class"WpfM20UpdateFW.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://sch…

使用adb shell 命令接收串口发送过来的16进制数据 或者 发送16进制数据

首先执行 adb root Adb shell 找到你要发送或者接收串口数据的设备 如果我们再android设备上调试串口的时候&#xff0c;有时我们需要使用adb shell命令&#xff0c;假设我们使用串口发送的数据是16进制&#xff0c;那么使用cat 这个设备节点&#xff0c;有可能显示的是乱码例…

第6章 循环神经网络

系列文章目录 第1章 绪论 第2章 机器学习概述 第3章 线性模型 第4章 前馈神经网络 第5章 卷积神经网络 第6章 循环神经网络 第7章 网络优化与正则化 第8章 注意力机制与外部记忆 第9章 无监督学习 第10章 模型独立的学习方式 第11章 概率图模型 第12章 深度信念网络 第13章 深…

新相微在科创板过会:计划募资约15亿元,2022年业绩开始下滑

11月22日&#xff0c;上海证券交易所科创板披露的信息显示&#xff0c;上海新相微电子股份有限公司&#xff08;下称“新相微”&#xff09;获得上市委会议通过。据贝多财经了解&#xff0c;新相微于2022年6月28日在科创板递交招股书。 本次冲刺科创板上市&#xff0c;新相微计…

企业架构LB-服务器的负载均衡之LVS实现

01_学习目标和内容 02_LVS介绍和ipvsadm管理工具安装 03_LVS常见工作方式和调度算法介绍 04_需要知道的几个概念名词 05_LVS-NAT方式实现的流程原理 06_配置NAT模式准备工作 在centos中建立网卡配置信息 07_NAT模型RS真实服务器的网关配置 08_NAT模型DR调度服务器规则和转发配…

2010年数学二真题复盘

高数部分 选择题 第一题 思路分析:本要主要考察间断点的概念与极限运算. ☆首先无穷间断点属于第二类间断点,则至少有一个点不存在,要么没定义,要么就是在某个点的左极限或者右极限上趋于无穷,可以先化简,找一些分母趋于0的,那么就是无穷间断点了。 第二题 思路分…

设计模式之美——多组合少继承

组合优于继承&#xff0c;多用组合少用继承。 继承举例 假设我们要设计一个关于鸟的类。我们将“鸟类”这样一个抽象的事物概念&#xff0c;定义为一个抽象类 AbstractBird。所有更细分的鸟&#xff0c;比如麻雀、鸽子、乌鸦等&#xff0c;都继承这个抽象类。 我们知道&…

web前端期末大作业——贵州山地旅游介绍网页1页 HTML旅游网站设计与实现

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…