异常处理(二)

异常处理(二)

一、相关日志

1、C++发展后期增加的工具

http://blog.163.com/zhoumhan_0351/blog/static/3995422720101247214441

2、C++基础笔记(三)

http://blog.163.com/zhoumhan_0351/blog/static/39954227201012471612497

3、异常处理

http://blog.163.com/zhoumhan_0351/blog/static/3995422720104881216900

二、异常处理

1、auto_ptr

Since dynamic memory is the most frequent resource used in a typical 

C++ program, the standard provides an RAII wrapper for pointers to heap 

memory that automatically frees the memory. The auto_ptr class template, 

defined in the <memory> header, has a constructor that takes a pointer to its 

generic type (whatever you use in your code). The auto_ptr class template 

also overloads the pointer operators * and -> to forward these operations to 

the original pointer the auto_ptr object is holding. So you can use the 

auto_ptr object as if it were a raw pointer. 

#include <memory>

#include <iostream>

//#include <cstddef>

using namespace std;

class TraceHeap {

  int i;

public:

  static void* operator new(size_t siz) {

    void* p = ::operator new(siz);

    cout << "Allocating TraceHeap object on the heap "

         << "at address " << p << endl;

    return p;

  }

  static void operator delete(void* p) {

    cout << "Deleting TraceHeap object at address "

         << p << endl;

    ::operator delete(p);

  }

  TraceHeap(int i) : i(i) {}

  int getVal() const { return i; }

};

int main() {

  auto_ptr<TraceHeap> pMyObject(new TraceHeap(5));

  cout << pMyObject->getVal() << endl;  // Prints 5

} ///:~

The auto_ptr class template is also handy for pointer data members. Since 

class objects contained by value are always destructed, auto_ptr members 

always delete the raw pointer they wrap when the containing object is 

destructed.

template<class T>

    class auto_ptr {

public:

    typedef T element_type;

    explicit auto_ptr(T *p = 0) throw();

    auto_ptr(const auto_ptr<T>& rhs) throw();

    auto_ptr<T>& operator=(auto_ptr<T>& rhs) throw();

    ~auto_ptr();

    T& operator*() const throw();

    T *operator->() const throw();

    T *get() const throw();

    T *release() const throw();

    };

2、函数级的try块

Since constructors can routinely throw exceptions, you might want to handle 

exceptions that occur when an object’s member or base subobjects are 

initialized. To do this, you can place the initialization of such subobjects in a 

function-level try block. In a departure from the usual syntax, the try block for 

constructor initializers is the constructor body, and the associated catch block 

follows the body of the constructor, 

// Handles exceptions from subobjects.

#include <iostream>

using namespace std;

class Base {

  int i;

public:

  class BaseExcept {};

  Base(int i) : i(i) { throw BaseExcept(); }

};

class Derived : public Base {

public:

  class DerivedExcept {

    const char* msg;

  public:

    DerivedExcept(const char* msg) : msg(msg) {}

    const char* what() const { return msg; }

  };

  Derived(int j) try : Base(j) {

    // Constructor body

    cout << "This won't print" << endl;

  } catch(BaseExcept&) {

    throw DerivedExcept("Base subobject threw");;

  }

};

int main() {

  try {

    Derived d(3);

  } catch(Derived::DerivedExcept& d) {

    cout << d.what() << endl;  // "Base subobject threw"

  }

} ///:~

3、所有的标准异常类归根结底是从exception类派生的(defined in the header 

<exception>)。The two main derived classes are logic_error and 

runtime_error, which are found in <stdexcept> (which itself includes 

<exception>). The class logic_error represents errors in programming logic, 

such as passing an invalid argument. runtime errors are those that occur as 

the result of unforeseen forces such as hardware failure or memory 

exhaustion. Both runtime_error and logic_error provide a constructor that 

takes a std::string argument so that you can store a message in the exception 

object and extract it later with exception::what( ) 

#include <stdexcept>

#include <iostream>

using namespace std;

class MyError : public runtime_error {

public:

  MyError(const string& msg = "") : runtime_error(msg) {}

};

int main() {

  try {

    throw MyError("my message");

  } catch(MyError& x) {

    cout << x.what() << endl;

  }

} ///:~

注意到的是,Although the runtime_error constructor inserts the message 

into its std::exception subobject, std::exception does not provide a constructor 

that takes a std::string argument. You’ll usually want to derive your 

exception classes from either runtime_error or logic_error (or one of their 

derivatives), and not from std::exception.

ios::failure也是从exception派生来的,但是没有子类。

4、异常规格说明

    也就是函数声明的修饰符,写在函数声明的后面。

void f() throw(toobig, toosmall, divzero);//抛出列出的类型异常

void f();//抛出任何类型异常

void f() throw();//不会抛出异常

(1)unexcepted()函数

The special function unexpected( ) is called when you throw something other 

than what appears in the exception specification. Should this unfortunate 

situation occur, the default unexpected( ) calls the terminate( ) function 

described earlier in this chapter.

(2) set_unexpected( )

我们可以设计一种机制设置自己的函数来响应意外的异常。set_unexpected( ), 

which, like set_terminate( ), takes the address of a function with no arguments and 

void return value. Also, because it returns the previous value of the unexpected( ) 

pointer, you can save it and restore it later. To use set_unexpected( ), include the 

header file <exception>. 

#include <exception>

#include <iostream>

using namespace std;

class Up {};

class Fit {};

void g();

 

void f(int i) throw(Up, Fit) {

  switch(i) {

    case 1: throw Up();

    case 2: throw Fit();

  }

  g();

// void g() {}         // Version 1

void g() { throw 47; } // Version 2

void my_unexpected() {

  cout << "unexpected exception thrown" << endl;

  exit(0);

int main() {

  set_unexpected(my_unexpected); // (Ignores return value)

  for(int i = 1; i <=3; i++)

    try {

      f(i);

    } catch(Up) {

      cout << "Up caught" << endl;

    } catch(Fit) {

      cout << "Fit caught" << endl;

    }

} ///:~

If the exception thrown from your unexpected handler is not allowed by the 

original function’s specification, one of the following occurs:

1.  If std::bad_exception (defined in <exception>) was in the function’s 

exception specification, the exception thrown from the unexpected handler is 

replaced with a std::bad_exception object, and the search resumes from the 

function as before.

2.  If the original function’s specification did not include std::bad_exception, 

terminate( ) is called.

#include <exception>    // For std::bad_exception

#include <iostream>

#include <cstdio>

using namespace std;

// Exception classes:

class A {};

class B {};

// terminate() handler

void my_thandler() {

  cout << "terminate called" << endl;

  exit(0);

}

// unexpected() handlers

void my_uhandler1() { throw A(); }

void my_uhandler2() { throw; }

// If we embed this throw statement in f or g,

// the compiler detects the violation and reports

// an error, so we put it in its own function.

void t() { throw B(); }

void f() throw(A) { t(); }

void g() throw(A, bad_exception) { t(); }

int main() {

  set_terminate(my_thandler);

  set_unexpected(my_uhandler1);

  try {

    f();

  } catch(A&) {

    cout << "caught an A from f" << endl;

  }

  set_unexpected(my_uhandler2);

  try {

    g();

  } catch(bad_exception&) {

    cout << "caught a bad_exception from g" << endl;

  }

  try {

    f();

  } catch(...) {

    cout << "This will never print" << endl;

  }

} ///:~

注:当然例子并不一定全是正确的,只是作为参考。

(3)Each public function in a class essentially forms a contract with the user; 

if you pass it certain arguments, it will perform certain operations and/or 

return a result. The same contract must hold true in derived classes; otherwise 

the expected “is-a” relationship between derived and base classes is violated. 

Since exception specifications are logically part of a function’s declaration, 

they too must remain consistent across an inheritance hierarchy. 

    也就是说,派生类中抛出的异常,基类也一定可以处理,不然不符合is关系了。可以在派生类的异常规格说明中指定较少的异常或指定为不抛出异常。

Parameter types are not covariant—you are not allowed to change the 

signature of an overridden virtual function。

当无法确知会触发什么异常时,不要使用异常规格说明。异常规格说明主要用于非模板类。所以C++ STL没有异常处理。

内聚原则,每个函数只做一件事。不会默默的吞没异常的软件库被称为异常中立的。不要让析构函数抛出异常。

5、使用异常

(1)不要在异步事件中使用。

(2)不要用于程序的流程控制。

(3)如果用户自己的特定类创建异常类,最好在这个特定类中或包含这个特定类的名字空间中嵌套异常类。

(4)Multiple inheritance (MI)。the only essential place for MI is if you need to 

upcast an object pointer to two different base classes—that is, if you need 

polymorphic behavior with both of those base classes.

(5)不要析构函数中抛出异常

Because destructors are called in the process of throwing other exceptions, 

you’ll never want to throw an exception in a destructor or cause another 

exception to be thrown by some action you perform in the destructor. If this 

happens, a new exception can be thrown before the catch-clause for an 

existing exception is reached, which will cause a call to terminate( ).

If you call any functions inside a destructor that can throw exceptions, 

those calls should be within a try block in the destructor, and the destructor must handle all exceptions itself. None must escape from the destructor.

(6)避免悬挂指针

可以使用智能指针等,如auto_ptr来处理指向堆内存的指针。

(7)Whenever a function is called, information about that function is pushed 

onto the runtime stack in an activation record instance (ARI), also called a 

stack frame. A typical stack frame contains the address of the calling function 

(so execution can return to it), a pointer to the ARI of the function’s static 

parent (the scope that lexically contains the called function, so variables global 

to the function can be accessed), and a pointer to the function that called it (its 

dynamic parent). The path that logically results from repetitively following 

the dynamic parent links is the dynamic chain, or call chain. This is how 

execution can backtrack when an exception is thrown, and it is the mechanism 

that makes it possible for components developed without knowledge of one 

another to communicate errors at runtime.

Since information about exception-handling code and the offsets of local 

objects can be computed once at compile time, such information can be kept 

in a single place associated with each function, but not in each ARI. You 

essentially remove exception overhead from each ARI and thus avoid the 

extra time to push them onto the stack. This approach is called the zero-cost 

model of exception handling, and the optimized storage mentioned earlier is 

known as the shadow stack. 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值