构造函数可以返回NULL值吗?

时间:2010-05-18 16:16:42

标签: c++ constructor null return

我知道构造函数不会“返回”任何东西,但是例如,如果我调用CMyClass *object = new CMyClass()是否有任何方法可以使对象在构造函数失败时为NULL?在我的情况下,我有一些必须加载的图像,如果文件读取失败,我希望它返回null。有没有办法做到这一点?
提前谢谢。

15 个答案:

答案 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)

您不应该在构造函数中执行此类工作。构造函数应该执行绝对最少的工作量以使对象可用。