zoukankan      html  css  js  c++  java
  • 异常声明

    相比于断言适用于排除逻辑上不可能存在的状态,异常通常是用于逻辑上可能发生的错误。

    异常声明

    Item 1:当函数不可能抛出异常或不能接受抛出异常时,使用noexcept

    理由

    如果不打算抛出异常的话,程序就会认为无法处理这种错误,并且应当尽早终止,如此可以有效地阻止异常的传播与扩散。

    示例

    //不可能抛出异常时
    double compute(double d) noexcept
    {
        return log(sqrt(d <= 0 ? 1 : d));
    }
    

    这里,已知compute不会抛出异常,因为它仅由不会抛出异常的操作所组成。 通过将compute声明为noexcept,让编译器和读者获得信息,使其更容易理解和操作compute。

    注解

    许多标准库函数都是noexcept的,这包括所有从C标准库中“继承”来的标准库函数,C++标准库隐含地为这些函数标上了noexcept。

    示例

    //不能接受抛出异常时
    vector<double> munge(const vector<double>& v) noexcept
    {
        vector<double> v2(v.size());
        // ... 做一些事 ...
    }
    

    这里的noexcept表明不希望或无法处理vector对象抛出异常的情形。认为内存耗尽是一种严重的设计错误(类比于硬件故障),因此希望当其发生时让程序崩溃。

    注解

    在大多数程序中,大多数函数都会抛出异常(比如说,
    它们可能使用new,调用会抛出异常的函数,或者通过抛出异常
    来报告失败的库函数),因此请勿随意到处散布noexcept而不考虑清楚异常是否可以被处理。

    Item 2:C++11中析构函数和内存释放函数都被默认隐式地具备noexcept性质

    理由

    析构函数(无论是用户自定义的,还是编译器自动生成的)不能失败,若析构函数试图抛出异常来退出,这是一种设计错误,程序最好终止执行。

    示例

    struct A
    {
    	~A() {throw 1;}			//隐式地具备noexcept性质
    }
    
    struct B
    {
    	~B() noexcept(false) {throw 2;}	//noexcept(false),显式地声明为可以抛出异常
    }
    
    struct C
    {
    	B b;		//成员有noexcept(false)的析构函数,同样可以抛出异常
    }
    

    注解

    如果程序员显示地为析构函数指定了noexcept,或者类的基类或成员有noexcept(false)的析构函数,析构函数就不会再保持默认值(比如这里的B和C)。上例中的类A析构函数被默认为noexcept(true),可以阻止异常的扩散,而B和C均可以抛出异常。

    理由

    delete函数常被析构函数所调用,C++11默认将delete函数设置成noexcept,从而提高应用程序的安全性。

    示例

    void operator delete (void *) noexcept;
    void operator delete (void *) noexcept;
    

    Item 3:对于constexpr函数,移动操作,swap函数,应该加上noexcept

    理由

    constexpr函数在运行时执行时可能抛出异常,因此可能需要对其中的一些使用noexcept

    示例

    //constexpr函数
    constexpr int fac(int n) noexcept   
    {
        return (n>1) ? n*fac(n-1) : 1;
    }
    

    理由

    能够抛出异常的移动操作将违反大多数人的合理假设。不会抛出异常的移动操作可以更高效地被标准库和语言设施所利用。

    示例

    //移动操作
    
    //正例
    template<typename T>
    class Vector 
    {
    	// ...
    	Vector(Vector&& a) noexcept :elem{ a.elem }, sz{ a.sz } 
    	{ 
    		a.sz = 0; 
    		a.elem = nullptr; 
    	}
    	Vector& operator=(Vector&& a) noexcept 
    	{ 
    		elem = a.elem;
    		sz = a.sz; 
    		a.sz = 0; 
    		a.elem = nullptr; 
    	}
    	// ...
    public:
    	T * elem;
    	int sz;
    };
    这些操作不会抛出异常。
    
    //反例
    template<typename T>
    class Vector2 
    {
    	// ...
    	Vector2(Vector2&& a) 
    	{ 
    		*this = a; // 直接利用复制操作
    	}             
    	Vector2& operator=(Vector2&& a) 
    	{ 
    		*this = a; // 直接利用复制操作
    	}  
    	// ...
    public:
    	T * elem;
    	int sz;
    };
    Vector2 不仅低效,而且由于向量的复制需要分配内存而使其可能抛出异常。
    

    理由

    swap广泛地以假定永不失败的方式被使用,而且如果存在可能失败的 swap函数的话,程序也很难编写为可以正确工作。如果元素类型的 swap会失败的话,标准库的容器和算法也无法正确工作。

    示例

    //swap函数
    class Foo 
    {
    // ...
    public:
        void swap(Foo& rhs) noexcept
        {
            m1.swap(rhs.m1);
            std::swap(m2, rhs.m2);
        }
    private:
        Bar m1;
        int m2;
    };
    
    为调用者方便起见,可以在类型所在的相同命名空间中提供一个非成员的 swap 函数。
    
    void swap(Foo& a, Foo& b)
    {
        a.swap(b);
    }
    
  • 相关阅读:
    每日博客
    每日博客
    每日博客
    每日博客
    每日博客
    每日博客
    每日博客
    每日博客
    centos7 systemctl 管理MySQL
    Postgresqlz之迁移数据pg_dump
  • 原文地址:https://www.cnblogs.com/Stephen-Qin/p/9108935.html
Copyright © 2011-2022 走看看