如果构造函数中有错误,请停止进程

时间:2015-04-05 19:26:31

标签: c++ constructor

在实用程序类文件中,我想打开一个文件来读取或写入它 如果我无法打开它,我不想继续这个过程。

FileUtility::FileUtility(const char *fileName) {
  ifstream in_stream;
  in_stream.open(filename);
}

FileUtility fu = FileUtility("bob.txt");
fu.read();
fu.write();

文件bob.txt不存在,所以我不想让方法读写。

有干净的方法吗?

2 个答案:

答案 0 :(得分:4)

当在C ++中构造对象失败时,您应该抛出异常,或者从子对象的失败构造中传播异常。

FileUtility(const char* filename) {
    std::ifstream in_stream;
    in_stream.exceptions(std::ios_base::failbit);
    in_stream.open(filename); // will throw if file can't be opened
}

在调用代码中,您可以选择处理异常:

try {
    FileUtility fu = FileUtility("bob.txt");
} catch (std::ios_base::failure) {
    printf("Failed to open bob.txt\n");
    exit(EXIT_FAILURE);
}
// do other stuff

或者,如果你没有捕获异常,运行时只会调用std::terminate(),它将打印出自己的错误消息,这可能有用也可能没用:

terminate called after throwing an instance of 'std::ios_base::failure'
  what():  basic_ios::clear
Aborted

答案 1 :(得分:1)

通常有四种方式可以将错误状态从被叫方传递给呼叫者:

<强> 1。直接返回值(返回代码或OUT参数)。

虽然OUT参数是,但构造函数调用不能返回代码。但是,要求每个函数为此目的提供其返回代码或OUT参数有点侵入性,所以我不喜欢这个解决方案,尽管它肯定在各种库和API中大量使用。您可以通过向构造函数添加指针或引用参数来使用此方法,调用者可以向其提供某些本地错误变量的地址,构造函数可以在其中存储可能的返回值。我不推荐这个。

<强> 2。异常。

在C ++代码和其他语言中,关于异常的利弊存在一些两极分化的争论。我可能会采取一些贬值来说这个,但我个人的意见是应该像瘟疫一样避免例外。有关分享我观点的人,请参阅http://www.joelonsoftware.com/items/2003/10/13.html。但如果你这么倾向,这是一个可行的解决方案。有关此解决方案的详细演示,请参阅@ Brian的答案。

第3。对象属性。

std::ifstream对象实际上是这样做的,所以你可以利用它。 (实际上,从您的示例代码中,您将std::ifstream定义为构造函数中的局部变量,这意味着它在调用后不会持续存在,但是因为您调用某种read()并且构造对象上的write()方法,意味着你在调用之后保留它,所以我将假设后者是正确的推理。)你可以利用它,致电std::ifstream::is_open()。如果您想要保留std::ifstream的封装,您可以在is_open()上定义自己的FileUtility等效项,只需返回in_stream.is_open();,假设它保留为属性在FileUtility课程上。

struct FileUtility {
    ifstream ifs;
    FileUtility(const char* fileName);
    bool is_open(void) const;
};

FileUtility::FileUtility(const char* fileName) { ifs.open(fileName); }
bool FileUtility::is_open(void) const { return ifs.is_open(); }

FileUtility fu = FileUtility("bob.txt");
if (!fu.is_open()) return 1;

或者,您可以仅为FileUtility类创建一个全新的错误状态图层,并通过它传播std::ifstream错误。例如:

struct FileUtility {
    static const int ERROR_NONE = 0;
    static const int ERROR_BADFILE = 1;
    ifstream ifs;
    int error;
    FileUtility(const char* fileName);
};

FileUtility::FileUtility(const char* fileName) : error(ERROR_NONE) {
    ifs.open(fileName);
    if (!ifs.is_open()) { error = ERROR_BADFILE; return; }
}

FileUtility fu = FileUtility("bob.txt");
if (fu.error != FileUtility::ERROR_NONE) return 1;

这些是合理的解决方案。

<强> 4。全局错误状态。

如果一些程序员用#34响应,我不会感到惊讶,这听起来像个坏主意&#34;对这种可能的解决方案的反应,但事实是许多非常成功和突出的代码库使用此解决方案来传达错误状态。也许最好的例子是C标准库使用的errno变量(虽然应该提到errno与直接返回码一起使用),并使用GetLastError()系统通过Windows C API。我想有些人可能认为这确实是&#34; C方法&#34;,例外是&#34; C ++方法&#34;,但同样,我避免像瘟疫这样的例外。

顺便说一句,多线程不是此解决方案的问题,因为errnoGetLastError()都使用线程局部错误状态,而不是真正的全局错误状态。

我最喜欢这个解决方案,因为它简单,非常无创,并且可以很容易地被不同的代码库重用,前提是您定义了错误框架(基本上是线程局部变量,可能是{ {1}}宏/全局;见下文)在自己的库中,在这种情况下,代码在处理错误时会获得一致性。

示例:

ERROR_NONE

这是我推荐的解决方案;否则我会使用对象属性解决方案。

相关问题