gpt4 book ai didi

c++ - 从 C `goto` 错误处理范例转换到 C++ 异常处理范例

转载 作者:IT老高 更新时间:2023-10-28 21:38:18 26 4
gpt4 key购买 nike

我是一名学习 C++ 的 C 程序员。在 C 中,有一个常见的 goto idiom used to handle errors and exit cleanly from a function .我读过通过 try-catch block 处理异常是面向对象程序中的首选,但我在 C++ 中实现这个范例时遇到了麻烦。

以 C 中的以下函数为例,它使用 goto 错误处理范例:

unsigned foobar(void){
FILE *fp = fopen("blah.txt", "r");
if(!fp){
goto exit_fopen;
}

/* the blackbox function performs various
* operations on, and otherwise modifies,
* the state of external data structures */
if(blackbox()){
goto exit_blackbox;
}

const size_t NUM_DATUM = 42;
unsigned long *data = malloc(NUM_DATUM*sizeof(*data));
if(!data){
goto exit_data;
}

for(size_t i = 0; i < NUM_DATUM; i++){
char buffer[256] = "";
if(!fgets(buffer, sizeof(buffer), fp)){
goto exit_read;
}

data[i] = strtoul(buffer, NULL, 0);
}

for(size_t i = 0; i < NUM_DATUM/2; i++){
printf("%lu\n", data[i] + data[i + NUM_DATUM/2]);
}

free(data)
/* the undo_blackbox function reverts the
* changes made by the blackbox function */
undo_blackbox();
fclose(fp);

return 0;

exit_read:
free(data);
exit_data:
undo_blackbox();
exit_blackbox:
fclose(fp);
exit_fopen:
return 1;
}

我尝试使用异常处理范例在 C++ 中重新创建函数:

unsigned foobar(){
ifstream fp ("blah.txt");
if(!fp.is_open()){
return 1;
}

try{
// the blackbox function performs various
// operations on, and otherwise modifies,
// the state of external data structures
blackbox();
}catch(...){
fp.close();
return 1;
}

const size_t NUM_DATUM = 42;
unsigned long *data;
try{
data = new unsigned long [NUM_DATUM];
}catch(...){
// the undo_blackbox function reverts the
// changes made by the blackbox function
undo_blackbox();
fp.close();
return 1;
}

for(size_t i = 0; i < NUM_DATUM; i++){
string buffer;
if(!getline(fp, buffer)){
delete[] data;
undo_blackbox();
fp.close();
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;
}

delete[] data;
undo_blackbox();
fp.close();

return 0;
}

我觉得我的 C++ 版本没有正确实现异常处理范例;事实上,由于随着函数的增长,catch block 中积累了清理代码,因此 C++ 版本的可读性更差,更容易出错。

我已经读到,由于称为 RAII 的东西,catch block 中的所有这些清理代码在 C++ 中可能是不必要的,但我不熟悉这个概念。 我的实现是否正确,或者有更好的方法来处理错误并干净地退出 C++ 中的函数?

最佳答案

RAII的原理|是您使用类类型来管理使用后需要清理的任何资源;清理是由析构函数完成的。

这意味着您可以创建一个本地 RAII 管理器,该管理器会在超出范围时自动清理它所管理的任何内容,无论是由于正常的程序流程还是异常。 catch block 应该永远不需要只是为了清理;仅当您需要处理或报告异常时。

在您的情况下,您拥有三个资源:

  • 文件fpifstream 已经是 RAII 类型,所以只需删除对 fp.close() 的冗余调用,一切都很好。
  • 分配的内存data。如果它是一个小的固定大小(这样),则使用本地数组,如果需要动态分配,则使用 std::vector;然后摆脱 delete.
  • blackbox设置的状态。

您可以为“黑盒”恶意代码编写自己的 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;
}
}

关于c++ - 从 C `goto` 错误处理范例转换到 C++ 异常处理范例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28631167/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com