我想在类构造函数中打开一个文件.开口可能会失败,然后无法完成对象构造.如何处理这个失败?抛出异常?如果这是可能的,如何在非抛出构造函数中处理它?
BЈо*_*вић 36
如果对象构造失败,则抛出异常.
替代方案很可怕.如果构造成功,则必须创建一个标志,并在每个方法中检查它.
Ton*_*roy 27
我想在类构造函数中打开一个文件.开口可能会失败,然后无法完成对象构造.如何处理这个失败?抛出异常?
是.
如果这是可能的,如何在非抛出构造函数中处理它?
你的选择是:
if (X x) ...即可以在布尔上下文中评估对象,通常通过提供operator bool() const或类似的整数转换),但是你没有x范围查询错误的详细信息.这可能是例如熟悉的if (std::ifstream f(filename)) { ... } else ...;bool worked; X x(&worked); if (worked) ...
if (X* p = x_factory()) ...</li>
<li>X x; //永远不会用 if(init_x(&x))...`简而言之,C++旨在为这些问题提供优雅的解决方案:在这种情况下,异常.如果你人为地限制自己使用它们,那么不要指望有其他东西可以做一半好的工作.
(PS我喜欢传递将被指针修改的变量 - worked如上所述 - 我知道FAQ lite不鼓励它但不同意推理.除非你没有涉及FAQ的内容,否则对它的讨论并不特别感兴趣.)
jco*_*der 15
我对这种特定情况的建议是,如果你不想让一个constuctor失败,因为如果无法打开一个文件,那么就避免这种情况.将已经打开的文件传递给构造函数,如果这是你想要的,那么它就不会失败......
lor*_*rro 13
新的C++标准在很多方面重新定义了它,以便重新审视这个问题.
最佳选择:
命名可选:有一个最小的私有构造函数和一个命名构造函数:static std::experimental::optional<T> construct(...).后者尝试设置成员字段,确保不变,只有当它肯定会成功时才调用私有构造函数.私有构造函数仅填充成员字段.它很容易测试可选项并且价格便宜(即使是复制也可以很好地实现).
功能风格:好消息是,(非命名)构造函数永远不是虚拟的.因此,您可以使用静态模板成员函数替换它们,该函数除了构造函数参数之外还需要两个(或更多)lambdas:如果成功则为1,如果失败则为1.'真正的'构造函数仍然是私有的,不能失败.这可能听起来有点矫枉过正,但编译器会对lambdas进行精彩优化.您甚至if可以通过这种方式省去可选项.
好的选择:
例外:如果所有其他方法都失败,请使用异常 - 但请注意,在静态初始化期间无法捕获异常.一种可能的解决方法是在这种情况下让函数的返回值初始化对象.
Builder类:如果构造很复杂,那么有一个类可以进行验证,并且可能需要进行一些预处理,以确保操作不会失败.让它有办法返回状态(是的,错误功能).我个人只是堆叠,所以人们不会传递它; 然后让它有一个.build()构造另一个类的方法.如果构建器是朋友,构造函数可以是私有的.甚至可能只需要构建器可以构造的内容,以便记录此构造函数仅由构建器调用.
糟糕的选择:(但多次见过)
标志:不要因为"无效"状态而弄乱你的班级不变量.这正是我们拥有的原因optional<>.想想optional<T>那可能是无效的,T那不可能.仅适用于有效对象的(成员或全局)函数T.一个肯定会返回有效的作品T.可能返回无效对象的一个返回optional<T>.可能使对象无效的对象采用非const optional<T>&或optional<T>*.这样,您就不需要检查对象有效的每个函数(并且那些if可能变得有点贵),但是在构造函数中也不会失败.
默认构造和setter:这与Flag基本相同,只是这次你被迫拥有一个可变模式.忘记了setter,他们不必要地使你的类不变复杂化.记住保持你的课堂简单,而不是简单的建设.
默认构造,init()它接受一个ctor参数:这没有什么比一个返回一个的函数更好optional<>,但需要两个构造并弄乱你的不变量.
拿bool& succeed:这是我们以前做的optional<>.原因optional<>是优越的,你不能错误地(或不小心!)忽略succeed标志并继续使用部分构造的对象.
返回指针的工厂:这不太通用,因为它强制动态分配对象.您返回给定类型的托管指针(因此限制分配/作用域架构)或返回裸ptr并冒险客户端泄漏.此外,随着移动原理图的性能提升,这可能会变得不那么令人满意(本地人,当保持堆栈时,非常快且缓存友好).
例:
#include <iostream>
#include <experimental/optional>
#include <cmath>
class C
{
public:
friend std::ostream& operator<<(std::ostream& os, const C& c)
{
return os << c.m_d << " " << c.m_sqrtd;
}
static std::experimental::optional<C> construct(const double d)
{
if (d>=0)
return C(d, sqrt(d));
return std::experimental::nullopt;
}
template<typename Success, typename Failed>
static auto if_construct(const double d, Success success, Failed failed = []{})
{
return d>=0? success( C(d, sqrt(d)) ): failed();
}
/*C(const double d)
: m_d(d), m_sqrtd(d>=0? sqrt(d): throw std::logic_error("C: Negative d"))
{
}*/
private:
C(const double d, const double sqrtd)
: m_d(d), m_sqrtd(sqrtd)
{
}
double m_d;
double m_sqrtd;
};
int main()
{
const double d = 2.0; // -1.0
// method 1. Named optional
if (auto&& COpt = C::construct(d))
{
C& c = *COpt;
std::cout << c << std::endl;
}
else
{
std::cout << "Error in 1." << std::endl;
}
// method 2. Functional style
C::if_construct(d, [&](C c)
{
std::cout << c << std::endl;
},
[]
{
std::cout << "Error in 2." << std::endl;
});
}
Run Code Online (Sandbox Code Playgroud)
一种方法是抛出异常。另一个方法是使用“bool is_open()”或“bool is_valid()”函数,如果构造函数中出现问题,该函数将返回 false。
这里的一些评论说在构造函数中打开文件是错误的。我要指出 ifstream 是 C++ 标准的一部分,它具有以下构造函数:
explicit ifstream ( const char * filename, ios_base::openmode mode = ios_base::in );
Run Code Online (Sandbox Code Playgroud)
它不会抛出异常,但它有一个 is_open 函数:
bool is_open ( );
Run Code Online (Sandbox Code Playgroud)
我想在类构造函数中打开一个文件。
几乎可以肯定这是一个坏主意。在极少数情况下,在施工期间打开文件是合适的。
有可能打开失败,导致对象构建无法完成。如何处理此故障?抛出异常?
是的,就是这样。
如果这是可能的,如何在非抛出构造函数中处理它?
使类的完整构造对象可能无效。这意味着提供验证例程、使用它们等等......ick
| 归档时间: |
|
| 查看次数: |
44584 次 |
| 最近记录: |