从C`goto`错误处理范式过渡到C++;异常处理范例 我是一个学习C++的C程序员。在C语言中,有一个共同点。通过代码>我读过异常处理,尝试 >代码> catch >代码>在面向对象程序中是首选的,但是在C++中实现这个范例有困难。
以C语言中使用从C`goto`错误处理范式过渡到C++;异常处理范例 我是一个学习C++的C程序员。在C语言中,有一个共同点。通过代码>我读过异常处理,尝试 >代码> catch >代码>在面向对象程序中是首选的,但是在C++中实现这个范例有困难。,c++,c,exception,error-handling,exception-handling,C++,C,Exception,Error Handling,Exception Handling,以C语言中使用goto错误处理范例的以下函数为例: unsigned foobar(void){ 文件*fp=fopen(“blah.txt”、“r”); 如果(!fp){ 转到福彭出口; } /*blackbox函数执行各种操作 *上的操作,并以其他方式修改, *外部数据结构的状态*/ if(blackbox()){ 转到黑匣子出口; } 常数大小数值基准=42; 无符号long*data=malloc(NUM_DATUM*sizeof(*data)); 如果(!数据){ 转到退出_数据; }
goto
错误处理范例的以下函数为例:
unsigned foobar(void){
文件*fp=fopen(“blah.txt”、“r”);
如果(!fp){
转到福彭出口;
}
/*blackbox函数执行各种操作
*上的操作,并以其他方式修改,
*外部数据结构的状态*/
if(blackbox()){
转到黑匣子出口;
}
常数大小数值基准=42;
无符号long*data=malloc(NUM_DATUM*sizeof(*data));
如果(!数据){
转到退出_数据;
}
对于(大小i=0;i
我尝试用异常处理范式重新创建C++中的函数:
unsigned foobar(){
ifstream fp(“blah.txt”);
如果(!fp.is_open()){
返回1;
}
试一试{
//blackbox函数执行各种操作
//上的操作,并以其他方式修改,
//外部数据结构的状态
黑盒();
}捕获(…){
fp.close();
返回1;
}
常数大小数值基准=42;
无符号长*数据;
试一试{
数据=新的无符号长[NUM_DATUM];
}捕获(…){
//undo_blackbox函数恢复
//blackbox函数所做的更改
撤销_黑匣子();
fp.close();
返回1;
}
对于(大小i=0;i>数据[i];
}
对于(尺寸i=0;i
这意味着您可以创建一个本地RAII管理器,当它超出范围时,它将自动清理它正在管理的任何东西,无论是由于正常的程序流还是异常引起的。永远不应该仅仅为了清理而需要catch
块;只有当您需要处理或报告异常时才需要
在您的案例中,您有三种资源:
- 文件
fp
ifstream
已经是RAII类型,所以只需删除对fp.close()
的冗余调用,一切都很好
- 分配的内存
data
。如果它是一个小的固定大小(就是这样),则使用本地数组;如果需要动态分配,则使用std::vector
;然后去掉删除
- 由
blackbox
设置的状态
您可以为“黑盒”malarkey编写自己的RAII包装:
struct blackbox_guard {
// Set up the state on construction
blackbox_guard() {blackbox();}
// Restore the state on destruction
~blackbox_guard() {undo_blackbox();}
// Prevent copying per the Rule of Three
blackbox_guard(blackbox_guard const &) = delete;
void operator=(blackbox_guard) = delete;
};
现在,您可以删除所有错误处理代码;我希望通过异常(抛出或允许传播)来指示失败,而不是一个神奇的返回值,给出:
void foobar(){
ifstream fp ("blah.txt"); // No need to check now, the first read will fail if not open
blackbox_guard bb;
const size_t NUM_DATUM = 42;
unsigned long data[NUM_DATUM]; // or vector<unsigned long> data(NUM_DATUM);
for(size_t i = 0; i < NUM_DATUM; i++){
string buffer;
// You could avoid this check by setting the file to throw on error
// fp.exceptions(ios::badbit); or something like that before the loop
if(!getline(fp, buffer)){
throw std::runtime_error("Failed to read"); // or whatever
}
stringstream(buffer) >> data[i]; // or data[i] = stoul(buffer);
}
for(size_t i = 0; i < NUM_DATUM/2; i++){
cout << data[i] + data[i + NUM_DATUM/2] << endl;
}
}
void foobar(){
ifstream fp(“blah.txt”);//现在无需检查,如果未打开,第一次读取将失败
黑盒保护bb;
常数大小数值基准=42;
无符号长数据[NUM_DATUM];//或向量数据(NUM_DATUM);
对于(大小i=0;i>data[i];//或data[i]=stoul(buffer);
}
对于(尺寸i=0;i
核心思想是在初始化对象的过程中获取资源,并设置对象,使其在销毁时正确释放资源。这一工作的关键点是,当通过异常退出作用域时,析构函数正常运行
在您的情况下,已经有RAII可用,您只是没有使用它。std::ifstream
(我假设这就是您的ifstream
所指的)确实会在销毁时关闭。因此可以安全地忽略catch
中的所有close()
对于数据
,您也应该使用RAII包装。有两种可用:std::unique_ptr
,和std::vector
。它们都负责各自析构函数中的内存释放
最后,对于blackbox()
,您可以自己创建一个简单的RAII包装器:
struct BlackBoxer
{
BlackBoxer()
{
blackbox();
}
~BlackBoxer()
{
undo_blackbox();
}
};
当使用这些代码重写时,您的代码将变得更加简单:
unsigned foobar() {
ifstream fp ("blah.txt");
if(!fp.is_open()){
return 1;
}
try {
BlackBoxer b;
const size_t NUM_DATUM = 42;
std::vector<unsigned long> data(NUM_DATUM);
for(size_t i = 0; i < NUM_DATUM; i++){
string buffer;
if(!getline(fp, buffer)){
return 1;
}
stringstream(buffer) >> data[i];
}
for(size_t i = 0; i < NUM_DATUM/2; i++){
cout << data[i] + data[i + NUM_DATUM/2] << endl;
}
return 0;
} catch (...) {
return 1;
}
}
unsigned foobar(){
ifstream fp(“blah.txt”);
如果(!fp.is_open()){
返回1;
}
试一试{
黑盒b;
常数大小数值基准=42;
std::矢量数据(NUM_数据);
对于(大小i=0;i>数据[i];
}
// void return type, we may no guarantees about exceptions
// this function may throw
void foobar(){
// the blackbox function performs various
// operations on, and otherwise modifies,
// the state of external data structures
blackbox();
// scope exit will cleanup blackbox no matter what happens
// a scope exit like this one should always be used
// immediately after the resource that it is guarding is
// taken.
// but if you find yourself using this in multiple places
// wrapping blackbox in a dedicated wrapper is a good idea
BOOST_SCOPE_EXIT[]{
undo_blackbox();
}BOOST_SCOPE_EXIT_END
const size_t NUM_DATUM = 42;
// using a vector the data will always be freed
std::vector<unsigned long> data;
// prevent multiple allocations by reserving what we expect to use
data.reserve(NUM_DATUM);
unsigned long d;
size_t count = 0;
// never declare things before you're just about to use them
// doing so means paying no cost for construction and
// destruction if something above fails
ifstream fp ("blah.txt");
// no need for a stringstream we can check to see if the
// file open succeeded and if the operation succeeded
// by just getting the truthy answer from the input operation
while(fp >> d && count < NUM_DATUM)
{
// places the item at the back of the vector directly
// this may also expand the vector but we have already
// reserved the space so that shouldn't happen
data.emplace_back(d);
++count;
}
for(size_t i = 0; i < NUM_DATUM/2; i++){
cout << data[i] + data[i + NUM_DATUM/2] << endl;
}
}
#include <fstream>
#include <stdexcept>
#include <vector>
// C or low-level functions to be wrapped:
int blackbox();
void undo_blackbox();
// just to be able to compile this example:
FILE *fp;
// The only self-made RAII class we need for this example
struct Blackbox {
Blackbox() {
if (!blackbox()) {
throw std::runtime_error("blackbox failed");
}
}
// Destructor performs cleanup:
~Blackbox() {
undo_blackbox();
}
};
void foobar(void){
// std::ifstream is an implementation of the RAII idiom,
// because its destructor closes the file:
std::ifstream is("blah.txt");
if (!is) {
throw std::runtime_error("could not open blah.txt");
}
Blackbox local_blackbox;
// std::vector itself is an implementation of the RAII idiom,
// because its destructor frees any allocated data:
std::vector<unsigned long> data(42);
for(size_t i = 0; i < data.size(); i++){
char buffer[256] = "";
if(!fgets(buffer, sizeof(buffer), fp)){
throw std::runtime_error("fgets error");
}
data[i] = strtoul(buffer, NULL, 0);
}
for(size_t i = 0; i < (data.size()/2); i++){
printf("%lu\n", data[i] + data[i + (data.size()/2)]);
}
// nothing to do here - the destructors do all the work!
}