我知道构造函数不会“返回”任何东西,但是例如,如果我调用CMyClass *object = new CMyClass()
是否有任何方法可以使对象在构造函数失败时为NULL?在我的情况下,我有一些必须加载的图像,如果文件读取失败,我希望它返回null。有没有办法做到这一点?
提前谢谢。
答案 0 :(得分:26)
我同意其他人你应该使用异常,但如果你确实需要因某些原因使用NULL,请将构造函数设为私有并使用工厂方法:
static CMyClass* CMyClass::create();
这意味着您无法正常构造实例,并且您无法再将它们分配到堆栈中,这是一个非常大的缺点。
答案 1 :(得分:21)
构造函数不返回值。它们初始化一个对象,报告错误的唯一方法是通过异常。
请注意,构造函数不进行任何类型的内存管理。内存分配,然后调用构造函数初始化它。并且该内存可以动态分配(type *x = new type;
),但它也可能位于堆栈(type x;
)或更复杂类型的子对象中。除了第一种情况之外,null都没有意义。
答案 2 :(得分:9)
“正确”**方式是抛出异常。
**您可以提供像is_valid
这样的成员函数,您可以在构造对象后检查它,但这在C ++中不是惯用的。
答案 3 :(得分:6)
执行此操作的方法是,如果在构造函数中发现某些不起作用,则应抛出异常。如果C ++无法为您的对象分配内存,则会发生这种情况 - 它会抛出std :: bad_alloc。您应该使用std :: exception或子类。
答案 4 :(得分:3)
可以使用静态工厂方法吗?在类型之间进行转换时,我可能会创建一个公共静态CMyClass转换(原始),如果original为null,则返回null。您可能仍希望为无效数据抛出异常。
答案 5 :(得分:1)
味道不好。
好吧,如果你真的想要这样做,重载new,新调用一个不进行初始化的私有构造函数,在new中进行初始化,如果初始化失败则返回null。
答案 6 :(得分:1)
不是告诉你如何让构造函数返回null,或者如何伪造它,让我建议一个替代方法:提供一种避免抛出异常的方法,例如通过延迟初始化或非抛出构造函数。但是,一旦执行此操作,您需要有一种方法来检查有效性,并确保任何使用无效实例的尝试都会引发异常。换句话说,你是在推迟异常,而不是完全避免它。
以下是: 您已经有一个构造函数,它接受一个文件路径并加载它,导致失败。将内容移动到一个Load方法,该方法获取文件路径并返回bool以指示成功。然后更改构造函数,以便它只调用Load并抛出false。在Load中,如果实例已正确初始化,请确保立即返回false。然后添加一个默认的析构函数和一个IsValid方法。
Per Dennis: 现在添加第二个构造函数,它接受一个布尔值来控制是否抛出异常,并考虑将Load降级为private,在这种情况下,您同样会删除默认构造函数。
这可以为您提供所需的一切,而无需制作无法维护的代码。看起来应该是这样的:
// Per Dennis, should go away if Load becomes private.
Image()
{
_valid = false;
}
Image(const string& filepath)
{
if (!Load(filepath))
throw new exception("Cannot open image.");
}
// Per Dennis.
Image(const string& filepath, bool doThrow)
{
if (!Load(filepath) && doThrow)
throw new exception("Cannot open image.");
}
// Per Dennis, this should probably be made private now.
bool Load(const string& filepath)
{
if (_valid)
return false;
// Try to load...
_valid = WhetherItLoadedExpression;
return _valid;
}
bool IsValid()
{
return _valid;
}
void Draw()
{
if (!IsValid())
throw new exception("Invalid object.");
// Draw...
}
修改的
请参阅下文,了解对丹尼斯的评论所做的更改。
答案 7 :(得分:1)
如果我调用
CMyClass* object = new CMyClass()
是否有任何方法可以在构造函数失败时使对象为NULL?
我明白你的意思了!有很多C ++库大量使用动态分配的内存并实现这个想法(例如QtGstreamer),所以它绝对可以编写你的代码:
CMyClass* object = new CMyClass()
if (!object)
{
// FAILED!
}
但是,它不是返回NULL
的对象的构造函数。它是operator new
的重载版本。
答案 8 :(得分:1)
通过覆盖新的运算符
可以做一点点hackish见这个例子:
http://coliru.stacked-crooked.com/a/62e097827724f91e
从技术上讲,它不再是构造函数,但它确实按照你想要的方式运行。
答案 9 :(得分:0)
在Visual C ++ 6中,内存饥饿的默认行为是新操作符返回NULL而不是抛出异常。这不是后来在C ++中标准化的行为,也不是现代C ++中的惯用语。
但是你肯定可以创建一个operator new版本,如果你愿意,可以采用这种方式行事,或使用nothrow
变体:if ( Foo * foo = new ( std::nothrow ) Foo ) { ... }
。
答案 10 :(得分:0)
实际上,你可以使用std :: nothrow使new返回0,但这只会导致它在内存分配失败时返回0。一旦它到达你的构造函数,就没有办法得到你想要的东西。
您应该将课堂上的问题分开。一个构造函数几乎不应该(我很想说'永远'期间,但我会留下我无法想到的罕见异常的空间)进行文件处理,除非文件处理是它的唯一责任(例如fstream)。
答案 11 :(得分:0)
您可以使用malloc
代替new
,因为malloc
不会抛出异常。在使用指针之前,您必须测试malloc
的结果。此外,如果malloc
成功,则必须初始化对象。
malloc
不会调用对象的构造函数。
答案 12 :(得分:0)
例外是你最好的选择。
如果您在Unix环境中编程,也可以检查errno
的值。
答案 13 :(得分:0)
当前消息应该是对答案的注释,但是没有一个查看代码的好地方!
我偶然发现了以下代码:
std::ofstream f(m_filename);
if (!f)
return false;
看到它实际上传递了return false;
语句。并说:“什么?构造函数返回NULL
吗?”
我被误导了,因为if
隐式调用了operator bool()
类的ofstream
。
答案 14 :(得分:-3)
您不应该在构造函数中执行此类工作。构造函数应该执行绝对最少的工作量以使对象可用。