使用异常抛出构造函数初始化对象的正确方法

时间:2009-06-30 15:13:31

标签: c++ exception constructor

这似乎是一个微不足道的问题,但我现在已经挂了几个小时了(也许是Java太多了我的C ++ braincells)。

我创建了一个具有以下构造函数的类(即没有默认构造函数)

VACaptureSource::VACaptureSource( std::string inputType, std::string inputLocation ) {
    if( type == "" || location == "" ) {
    throw std::invalid_argument("Empty type or location in VACaptureSource()");
}
type = inputType;
location = inputLocation;

// Open the given media source using the appropriate OpenCV function.
if( type.compare("image") ) {
    frame = cvLoadImage( location.c_str() );
    if( !frame ) {
        throw std::runtime_error("error opening file");
    }
}
else {
    throw std::invalid_argument("Unknown input type in VACaptureSource()");
}

}

当我想创建一个实例时,我使用

    // Create input data object
try {
    VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
catch( invalid_argument& ia ) {
    cerr << "FD Error: " << ia.what() << endl;
    usage(argv[0]);
}
catch( runtime_error& re ) {
    cerr << "FD Error: " << re.what() << endl;
    usage(argv[0]);
}

但是,在这种情况下,实例是此块的本地实例,我无法在其他任何地方引用它。另一方面,我不能说

VACAptureSource input;

在程序开头,因为没有默认构造函数。

这样做的正确方法是什么?

谢谢!

9 个答案:

答案 0 :(得分:11)

为什么你需要在try区块之外引用它?

而不是

try {
  VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
//catch....

//do stuff with input

您可以将所有内容移动到try块中:

try {
  VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
  //do stuff with input
}
//catch....

或者您可以将它分解为一个单独的函数,该函数从try块调用:

void doStuff(VACaptureSource& input){
  //do stuff with input
}

try {
  VACaptureSource input = VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
  doStuff(input);
}
//catch....

最后一个甚至为您提供了将构造与使用分开的好处,它可以很好地放入单元测试中,您可能希望函数在模拟对象上工作。

答案 1 :(得分:8)

使用指针(或其某些RAII版本)怎么样?

VACaptureSource* input = NULL;

try {
    input = new VACaptureSource(...);
} catch(...) {
    //error handling
}

//And, of course, at the end of the program
delete input;

答案 2 :(得分:4)

一个局部变量的范围限定在它所分配的块中(如Java),但是一旦块结束它就会破坏(与Java不同)所以你应该在try块本身中做你想要的所有东西(如果你只想处理构造函数异常,这可能是不可取的,或者你应该在其他地方(例如堆)分配对象,并使用父块中的指针来访问它。

答案 3 :(得分:3)

我可以观察到任何,但是最琐碎的构造函数可能会引发异常。因此,您不应该在某种意义上将异常视为“特殊”,而是编写代码以便自然处理它们。这意味着使用RAII以及此处其他答案提出的其他技术。

答案 4 :(得分:2)

您可以使用指针

VACaptureSource* input;
// Create input data object
try {
    input = new VACaptureSource("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");
}
catch( invalid_argument& ia ) {
    cerr << "FD Error: " << ia.what() << endl;
    usage(argv[0]);
}
catch( runtime_error& re ) {
    cerr << "FD Error: " << re.what() << endl;
    usage(argv[0]);
}

当你完成使用它时你需要释放对象

delete input

答案 5 :(得分:2)

  

我不能说

VACaptureSource input;
  

在程序开头,因为没有默认的构造函数。

有一个很好的理由你没有创建默认构造函数:即VACaptureSource仅在与文件关联时才有意义。所以不要创建默认构造函数。而只是简单地认识到VACaptureSource对象的范围是try块,并在那里使用它。

答案 6 :(得分:1)

添加一个使对象处于特殊的未配置状态的默认构造函数怎么样?然后有一个create()函数来实际创建它。

然后你可以这样做:

VACaptureSource input;
try
{
   input.create("image", "...");
}
catch(...)
{
   ...
}

根据情况,这可能比弄乱指针更好。虽然那时你还需要在做某事之前检查是否实际调用了create()......

答案 7 :(得分:1)

我实际上看不到任何问题:

我要更新的一些事情:

  • 通过const引用捕获异常。
  • 编译器可以优化代码中的复制结构 但没有它它看起来更整洁。只需使用参数声明输入。
  • 我会重构构造函数以获取const引用参数
    我会在初始化列表中初始化它们。
  • 另外,我会确保成员'frame'实际上是一个智能指针。

所以我会这样做(为了清楚起见)。

VACaptureSource::VACaptureSource( std::string const& inputType,
                                  std::string const& inputLocation )
      :type(inputType)
      ,location(inputLocation)
{
    // Other Code that throws.
}
void playWithCode()
{
    // Get input information from user.
    VACaptureSource input("image", "/home/cuneyt/workspace/testmedia/face_images/jhumpa_1.jpg");

    // use the input object.
    // Do not need to play with pointers here.
}
int main()
{
    try
    {
        playWithCode();
    }
    catch( invalid_argument const& ia )
    {    cerr << "FD Error: " << ia.what() << endl;
         usage(argv[0]);
    }
    catch( runtime_error const& re )
    {    cerr << "FD Error: " << re.what() << endl;
         usage(argv[0]);
    }
}

答案 8 :(得分:0)

简单。不要在构造函数中抛出异常。你不仅需要在try块中包装构造函数,在捕获异常时你将无法很好地处理内存(你是否调用了析构函数?需要删除多少类的内存? )

UPDATE0 :虽然如果您使用实例,我不确定内存管理是否有问题。

UPDATE1 :嗯,也许我正在考虑析构函数中的异常。

int
main2(int argc, char* argv[])
{
  MyClass class;
  class.doSomething();
}

int
main(int argc, char* argv[])
{
  int result = 0;
    try {
      main2(argc, argv);
    } catch (std::exception& se) {
      // oh noes!
       result = 1;
    }
  return result;
}