3

如何创建一个在失败时创建空对象的构造函数 (c++)。

我想创建一个行为类似于DCMTKDicomImage库中的类的类。

#include "diregist.h"   /* required to support color images */
DicomImage *image = new DicomImage("test.dcm");

if (image != NULL)
{
    if (image->getStatus() == EIS_Normal)
    {
        Uint8 *pixelData = (Uint8 *)(image->getOutputData(8 /* bits per sample */));

        if (pixelData != NULL)
        {
            /* do something useful with the pixel data */
        }
    } 
    else
        cerr << "Error: cannot load DICOM image (" << DicomImage::getString(image->getStatus()) << ")" << endl;
}
delete image;
4

6 回答 6

2

不容易。您可以DicomImage::operator new()在 DicomImage 类的声明中覆盖。然后,可能失败并返回 NULL 的实际逻辑将进入 operator new() 主体,而不是实际的构造函数。一旦进入构造函数,返回 NULL 为时已晚,此时对象已经创建。问题是 operator new() 不接收构造函数参数,因此您可能没有失败所需的信息。

另一种方法是让工厂方法或工厂类实际创建您的实例,因此您的代码如下所示:

DicomImage* pImage = CreateDicomImage( "stuff" );

但是,因为您的问题中包含“异常处理”......并且至少让答案更适合程序员 SE(而不是 stackoverflow)问题,我确实想指出您可以而且应该利用 C++ 中的异常处理。

当您返回 NULL 时,您会强制所有客户端代码在调用 new DicomImage() 之后立即添加错误检查逻辑,这会降低可读性,因为错误检查最终会与实际应用程序逻辑混合在一起。

拥有如下所示的代码不是更好吗?

std::unique_ptr< DicomImage > pImage( new DicomImage( "stuff" ) );
pImage->DoSomeCommand();
pImage->DoSomeOtherCommand();

...如果创建失败,不必担心“假设”条件。您可以通过使用 C++ 异常处理并让构造函数在创建失败时抛出异常来实现这一点。围绕处理这些东西的代码块设置 try-catch 块,而 try 块中的内容是纯应用程序逻辑。如果图像创建失败,则不会检查污染代码的错误,也不会崩溃。

...并且通过使用智能指针,您还可以保证如果以后发生任何故障,pImage 将始终被删除。因此,您无需担心在退出函数之前必须调用 delete。

于 2012-05-18T23:35:19.420 回答
1

使用 DicomImage 的代码并没有按照您的想法执行。

示例代码

if (image != NULL)
{

是针对无异常环境的防御性编程,当系统内存不足时返回 NULL。

启用异常后, new 将抛出内存不足异常。如果异常被禁用,那么 new 将返回 NULL。

要回答您的问题,构造函数不能返回 NULL。

但是,静态工厂方法可以返回 NULL。

class Foo
{
public:
    static Foo* MakeFoo()
    {
       Foo* foo = NULL;
       if (LifeIsGood())
           foo = new Foo();
       return foo;
    }
private:
    Foo();
};

Foo* myFoo = Foo::MakeFoo();
if (myFoo)
    BeFooish(myFoo);
于 2012-05-19T00:03:02.237 回答
0

我不认为构造函数可以做到这一点 - 这个 url 说构造函数不能返回 null

http://social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/1450ed84-277f-46d3-b2ea-8352986f877c

也许有一个“黑客”,但它可能是不合法的 你可能已经知道所有这些,但以防万一:(1)构造函数不能返回值,以及(2)我认为它没有任何控制权在分配给调用构造函数的实例的内存上,但构造函数可以将类成员设置为 NULL 值。

此 URL 有一个解决方法 - 如果您确实需要使用 NULL(不推荐)将构造函数设为私有并使用“工厂方法”(函数):

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

我知道这与你的问题是分开的,但我只是想确保有人知道编译器关于 new 的行为:

根据此 URL,最新的编译器不应该使用 new 运算符返回 NULL(应该抛出异常),尽管允许旧的编译器这样做:

http://www.parashift.com/c++-faq-lite/new/new-never-returns-null.html

于 2012-05-18T23:44:28.690 回答
0

您不能在创建对象时返回 null,但是您可以更改 operator == 和!= 的行为 尝试以下代码,我没有尝试过。

bool MyClass::operator==(const MyClass &other) const {
    if(&other == null){
        if(/* your validations */){
            return true;//it is null
        }else{
            return false;//
        }       
    }
    return (*this == other);//it is not compare with null
}
bool MyClass::operator!=(const MyClass &other) const {
    if(&other == null){
        if(/* your validations*/){
            return true;//is not null
        }else{
            return false;
        }       
    }
    return !(*this == other);//it is not compare with null
}
于 2012-05-20T03:27:51.703 回答
0

您不能,并且根据 的文档判断DicomImage,您真的不想模拟该类的接口。

于 2012-05-19T03:44:10.083 回答
0

使用 new() 运算符执行此操作并不是覆盖 new() 运算符的真正目的。从您的示例中,我不确定您是否允许更改客户端代码;但是,如果您可以控制客户端代码,则只需使用工厂方法即可使用构造函数:

class Foo {

  Foo(int arg) {...}

  static Foo *new_Foo(int arg) {
    if (! I_like(arg)) return null;
    return new Foo(arg);
  }

}

// ...
// in the client

Foo *foo =
// nope:
// new Foo(3);
// do this instead:
Foo.new_Foo(3);
于 2012-05-19T07:08:56.150 回答