构造函数可以返回 NULL 值吗?
我知道构造函数不会“返回”任何内容,但例如,如果我调用 CMyClass *object = new CMyClass()
如果构造函数失败,有没有办法使对象为 NULL?就我而言,我有一些必须加载的图像,如果文件读取失败,我希望它返回 null。有什么办法可以做到吗?
提前致谢。
I know constructors don't "return" anything but for instance if I call CMyClass *object = new CMyClass()
is there any way to make object to be NULL if the constructor fails? In my case I have some images that have to be loaded and if the file reading fails I'd like it to return null. Is there any way to do that?
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
我同意其他人的观点,你应该使用异常,但是如果你确实因为某种原因需要使用 NULL,请将构造函数设为私有并使用工厂方法:
这意味着你不能正常构造实例,而且你不能不再将它们分配在堆栈上,这是一个很大的缺点。
I agree with everyone else that you should use exceptions, but if you do really need to use NULL for some reason, make the constructor private and use a factory method:
This means you can't construct instances normally though, and you can't allocate them on the stack anymore, which is a pretty big downside.
构造函数不返回值。它们初始化一个对象,报告错误的唯一方法是通过异常。
请注意,构造函数不进行任何类型的内存管理。内存是外部分配的,然后调用构造函数进行初始化。并且该内存可以动态分配(
type *x = new type;
),但它也可能位于堆栈中(type x;
)或更多的子对象复杂类型。除了第一种情况之外,在所有情况下, null 根本没有意义。Constructors do not return values. They initialize an object and the only way of reporting errors is through an exception.
Note that the constructor does not make any type of memory management. Memory is allocated externally and then the constructor is called to initialize it. And that memory can be dynamically allocated (
type *x = new type;
) but it might as well be in the stack (type x;
) or a subobject of a more complex type. In all but the first case, null does not make sense at all.“正确”**的方法是抛出异常。
** 您可以提供像
is_valid
这样的成员函数,您可以在构造对象后检查该函数,但这在 C++ 中不是惯用的。The "correct"** way is to throw an exception.
** You can provide a member function like
is_valid
that you can check after constructing an object but that's just not idiomatic in C++.执行此操作的方法是,如果您发现构造函数中某些内容不起作用,则应该抛出异常。如果 C++ 无法为您的对象分配内存,就会发生这种情况 - 它会抛出 std::bad_alloc。您应该使用 std::exception 或子类。
The way to do this is if you find something not working in your constructor you should throw an exception. This is what happens if C++ cannot allocate memory for your object - it throws std::bad_alloc. You should use std::exception or a subclass.
可以使用静态工厂方法代替吗?在类型之间进行转换时,我可能会创建一个 public static CMyClass Convert(original) 并在original 为 null 时返回 null。不过,您可能仍然想针对无效数据抛出异常。
Could use a static factory method instead? When converting between types, I might make a public static CMyClass Convert(original) and return null if original is null. You'd probably still want to throw exceptions for invalid data though.
我不会告诉您如何让构造函数返回 null,或者如何伪造它,而是建议一个替代方案:提供一种避免抛出异常的方法,例如通过延迟初始化或不抛出构造函数。但是,一旦执行此操作,您就需要有一种方法来检查有效性并确保任何使用无效实例的尝试都会引发异常。换句话说,您是在延迟异常,而不是完全避免它。
方法如下:
您已经有一个构造函数,它接受文件路径并加载它,从而引发失败。将内容转移到 Load 方法中,该方法获取文件路径并返回一个布尔值以指示成功。然后更改构造函数,使其仅调用 Load 并抛出 false。在 Load 中,如果实例已正确初始化,请确保立即返回 false。然后添加默认析构函数和 IsValid 方法。
丹尼斯:
现在添加第二个构造函数,它采用布尔值来控制是否引发异常,并考虑将 Load 降级为私有,在这种情况下,您同样会删除默认构造函数。
这为您提供了您所要求的一切,而无需编写不可维护的代码。它应该看起来像这样:
编辑
请参阅下面针对丹尼斯的评论所做的更改。
Rather than telling you how to get a constructor to return null, or how to fake it, let me suggest an alternative: offer a way to avoid throwing an exception, such as by delayed initialization or a non-throwing constructor. Once you do this, though, you need to have a way to check validity and to ensure that any attempt to use an invalid instance does throw an exception. In other words, you're delaying the exception, not avoiding it entirely.
Here's how:
You already have a constructor that takes a file path and loads it, throwing on failure. Move the guts into a Load method that takes the file path and returns a bool to indicate success. Then change the constructor so it simply calls Load and throws on false. In Load, make sure to immediately return false if the instance is properly initialized. Then add a default destructor and an IsValid method.
Per Dennis:
Now add a second constructor that takes a boolean to control whether an exception is thrown, and consider relegating Load to private, in which case you would likewise remove the default constructor.
This gives you all that you can ask for, without making unmaintainable code. It should look something like this:
edit
See below for changes made in response to Dennis' comment.
品味不好。
如果你真的想这样做,重载 new,让 new 调用一个不进行初始化的私有构造函数,在 new 中进行初始化,如果初始化失败,让 new 返回 null。
In bad taste.
Well if you actually want to do this, overload new, have new call a private constructor that does no initialization, do the initialization in new, and have new return null if initialization fails.
我明白你的意思了!有很多 C++ 库大量使用动态分配的内存并实现了这一想法(例如 QtGstreamer),因此绝对可以将代码编写为如下所示:
但是,它不是返回
NULL
的对象的构造函数。它是operator new
的重载版本。I see what you mean! There are lots of C++ libraries out there that make heavy use of dynamically allocated memory and implement this idea (such as QtGstreamer), so its definitely possible to write your code to look like:
However, its not the constructor of the object that returns
NULL
. It's an overloaded version ofoperator new
.在 Visual C++ 6 中,内存不足时 new 运算符的默认行为是返回 NULL,而不是引发异常。这不是后来在 C++ 中标准化的行为,也不是现代 C++ 中的惯用行为。
但是,如果您愿意,您当然可以创建一个以这种方式运行的operator new 版本,或者使用nothrow 变体:if ( Foo * foo = new ( std::nothrow ) Foo ) { ... }。
In Visual C++ 6, the default behaviour on memory starvation was for the new operator to return NULL rather than throw an exception. This was not the behaviour later standardised in C++, nor idiomatic in modern C++.
But you certainly can create a version of operator new which behaves in that manner if you so wish, or use the
nothrow
variant :if ( Foo * foo = new ( std::nothrow ) Foo ) { ... }
.这可以通过覆盖 new 运算符来完成,
请参阅此示例:
http://coliru.stacked -crooked.com/a/62e097827724f91e
从技术上讲,它不再是构造函数,但它确实按照您想要的方式运行。
This can be done a little hackish by overriding the new operator
See this example:
http://coliru.stacked-crooked.com/a/62e097827724f91e
Technically it's no longer a constructor but it does behave the way you want it.
当前消息应该是对答案的评论,但是没有一个好的地方可以查看代码!
我偶然发现了以下代码:
并看到它实际上传递了
return false;声明。并说“什么?构造函数返回 NULL 吗?”
我被误导了,因为
if
隐式调用了ofstream
类的operator bool()
。Present message should be a comment on the answer, but there is not a good place to see code!
I've stumbled across the following code:
and saw it actually passing on the
return false;
statement. And said "What? Did the constructor returnNULL
?"I was being mislead, as the
if
makes an implicit call to theoperator bool()
of theofstream
class.实际上,您可以通过使用 std::nothrow 导致 new “返回”0,但这只会导致它在内存分配失败时返回 0。一旦它到达你的构造函数,就无法得到你想要的东西。
你应该将课堂上的关注点分开。构造函数几乎从不(我很想说“从不”,但我会为我想不到的罕见例外留出空间)进行文件处理,除非文件处理是其唯一的责任(例如 fstream)。
You can actually cause new to "return" 0 by using std::nothrow but this only causes it to return 0 if the memory allocation fails. Once it's gotten to your constructor there's no way to get what you want.
You should separate the concerns in your class. A constructor should almost never (I am tempted to say 'never' period but I'll leave room for the rare exception I can't think of) do file processing unless file processing is its sole responsibility (such as fstream).
您可以使用
malloc
而不是new
,因为malloc
不会引发异常。在使用指针之前,您必须测试malloc
的结果。另外,如果malloc
成功,您将必须初始化该对象。警告:
malloc
不会调用对象的构造函数。You could use
malloc
instead ofnew
sincemalloc
does not throw exceptions. You will have to test the result ofmalloc
before you use the pointer. Also, ifmalloc
succeeds, you will have to initialize the object.Warning:
malloc
does not call the object's constructor.例外是你最好的选择。
如果您在 Unix 环境中编程,您还可以检查 errno 的值。
Exceptions are your best bet.
You could also check the value of
errno
if you're programming in a Unix environment.您不应该在构造函数中执行此类工作。构造函数应该执行绝对最少的工作量以使对象可用。
You shouldn't perform such work in a constructor. Constructors should perform the absolute minimum amount of work to make an object usable.