要解决的问题:
- exception的定义
- throw的用法
设计习题
问题
- 自定义设计一个异常类?
- 列举c++中派生自exception的六个派生类。分别简短地说明它们的功能?
- throw,int类型和char*类型时,如何catch它们?
- catch块里为什么尽量用引用传递值而不是用值传递。那么指针传递呢?
- 如何在一个catch里面重新抛出该异常。?
- 捕获所有异常的两种方式?
- 如下写法存在什么问题?
catch (A& w) // 捕获Widget异常
{ // 处理异常
throw w; // 传递被捕获异常的拷贝
}
异常解释(exception)
当抛出一个异常时,程序会暂停当前函数的执行过程并开始寻找与异常相匹配的catch子句。当throw出现在一个try block时,检查与该try块关联的catch子句。如果找到。则进行处理。如果没找到,则继续检查与外层try匹配的catch子句。一直这样展开。直到找到为止。或者系统调用std::terminate退出主函数然后查找过程终止。这个过程称为 栈展开(stack unwinding)
在处理异常的过程中有个问题必须注意
栈展开过程中对象被自动销毁
在发生异常时,比如发生在构造函数中,可能有些成员已经初始化完了。有些成员还未初始化。则已构造的的成员,我们也要确保其能被正确地销毁。
在发生异常的函数中,析构函数总是会被执行,但是函数中释放资源的代码却可能被调用。(这是一个特性吧)
如果在析构函数中出现了异常。则可能导致该类异常退出。所以在析构函数中不应该抛出其不能处理的异常。如果确实要执行可能导致异常的操作,则该操作应该放在try语句块当中。
exception的定义
以下代码摘自faiss中的异常定义
# FaissException.h
#ifndef FAISS_EXCEPTION_INCLUDED
#define FAISS_EXCEPTION_INCLUDED
#include <exception>
#include <string>
namespace faiss {
/// Base class for Faiss exceptions
class FaissException : public std::exception {
public:
explicit FaissException(const std::string& msg);
FaissException(const std::string& msg,
const char* funcName,
const char* file,
int line);
/// from std::exception
const char* what() const noexcept override;
std::string msg;
};
/** bare-bones unique_ptr
* this one deletes with delete [] */
template<class T>
struct ScopeDeleter {
const T * ptr;
explicit ScopeDeleter (const T* ptr = nullptr): ptr (ptr) {}
void release () {ptr = nullptr; }
void set (const T * ptr_in) { ptr = ptr_in; }
void swap (ScopeDeleter<T> &other) {std::swap (ptr, other.ptr); }
~ScopeDeleter () {
delete [] ptr;
}
};
/** same but deletes with the simple delete (least common case) */
template<class T>
struct ScopeDeleter1 {
const T * ptr;
explicit ScopeDeleter1 (const T* ptr = nullptr): ptr (ptr) {}
void release () {ptr = nullptr; }
void set (const T * ptr_in) { ptr = ptr_in; }
void swap (ScopeDeleter1<T> &other) {std::swap (ptr, other.ptr); }
~ScopeDeleter1 () {
delete ptr;
}
};
}
#endif
以下代码为cpp文件
#include "FaissException.h"
namespace faiss {
FaissException::FaissException(const std::string& m)
: msg(m) {
}
FaissException::FaissException(const std::string& m,
const char* funcName,
const char* file,
int line) {
int size = snprintf(nullptr, 0, "Error in %s at %s:%d: %s",
funcName, file, line, m.c_str());
msg.resize(size + 1);
snprintf(&msg[0], msg.size(), "Error in %s at %s:%d: %s",
funcName, file, line, m.c_str());
}
const char*
FaissException::what() const noexcept {
return msg.c_str();
}
}
自定义异常的使用方式
FaissAssert.h
#ifndef FAISS_ASSERT_INCLUDED
#define FAISS_ASSERT_INCLUDED
#include "FaissException.h"
#include <cstdlib>
#include <cstdio>
#include <string>
///
/// Assertions
///
#define FAISS_ASSERT(X) \
do { \
if (! (X)) { \
fprintf(stderr, "Faiss assertion '%s' failed in %s " \
"at %s:%d\n", \
#X, __PRETTY_FUNCTION__, __FILE__, __LINE__); \
abort(); \
} \
} while (false)
#define FAISS_ASSERT_MSG(X, MSG) \
do { \
if (! (X)) { \
fprintf(stderr, "Faiss assertion '%s' failed in %s " \
"at %s:%d; details: " MSG "\n", \
#X, __PRETTY_FUNCTION__, __FILE__, __LINE__); \
abort(); \
} \
} while (false)
#define FAISS_ASSERT_FMT(X, FMT, ...) \
do { \
if (! (X)) { \
fprintf(stderr, "Faiss assertion '%s' failed in %s " \
"at %s:%d; details: " FMT "\n", \
#X, __PRETTY_FUNCTION__, __FILE__, __LINE__, __VA_ARGS__); \
abort(); \
} \
} while (false)
///
/// Exceptions for returning user errors
///
#define FAISS_THROW_MSG(MSG) \
do { \
throw FaissException(MSG, __PRETTY_FUNCTION__, __FILE__, __LINE__); \
} while (false)
#define FAISS_THROW_FMT(FMT, ...) \
do { \
std::string __s; \
int __size = snprintf(nullptr, 0, FMT, __VA_ARGS__); \
__s.resize(__size + 1); \
snprintf(&__s[0], __s.size(), FMT, __VA_ARGS__); \
throw FaissException(__s, __PRETTY_FUNCTION__, __FILE__, __LINE__); \
} while (false)
///
/// Exceptions thrown upon a conditional failure
///
#define FAISS_THROW_IF_NOT(X) \
do { \
if (!(X)) { \
FAISS_THROW_FMT("Error: '%s' failed", #X); \
} \
} while (false)
#define FAISS_THROW_IF_NOT_MSG(X, MSG) \
do { \
if (!(X)) { \
FAISS_THROW_FMT("Error: '%s' failed: " MSG, #X); \
} \
} while (false)
#define FAISS_THROW_IF_NOT_FMT(X, FMT, ...) \
do { \
if (!(X)) { \
FAISS_THROW_FMT("Error: '%s' failed: " FMT, #X, __VA_ARGS__); \
} \
} while (false)
#endif
在这边再简单介绍下c++的其他自己定义的异常。

throw的用法
throw 单独使用
throw 后面如果直接抛出异常
# 举几个例子
# try {
# 语句组
# }
# catch(异常类型) {
# 异常处理代码
# }
# ...
# catch(异常类型) {
# 异常处理代码
# }
try{
char str[] = "http://c.biancheng.net";
throw str; //数组类型
}
catch(char* p){
}
try {
char *pstr = str;
throw pstr; //指针类型
}
catch(
}
catch(char* p){
}
{
class Base{};
Base obj;
try {
throw obj; //对象类型
}catch(Base& b){
dosomething();
}
}
try {
throw 100; //int 类型
}
catch(int i){
dosomething();
}
网友评论