2009-06-30 45 views
5

这似乎是一个微不足道的问题,但现在我挂了几个小时(可能太多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; 

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

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

谢谢!

+1

你会如何在Java中解决这个问题?应用相同的解决方案。这个问题不依赖于语言。不同的语言只为不同的语法提供了不同的语法来实现等效目的,考虑到当你在Java中定义一个变量时,你不是声明一个对象,而是一个_reference_(C++术语中的指针),所以在Java和C++中看起来类似的代码并不是真正等效的代码。 – 2009-06-30 22:15:23

回答

8

如何使用指针(或其某些RAII版本)?

VACaptureSource* input = NULL; 

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

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

谢谢,我想过那个,但是认为C++中的指针“邪恶”,即仅用作最后的手段?除了使用指针之外没有别的办法吗? – recipriversexclusion 2009-06-30 15:37:53

+3

“邪恶”是一个非常强大的词。但是,正如CAdaker所建议的那样,RAII(即auto-destructing)变体(例如std :: auto_ptr)或任何适当的Boost自动指针都是明智的选择。 – 2009-06-30 15:55:17

+0

顺便说一下,你可能只想使用有效的实例,这意味着你可能想在完成需要完成的任务后重新从catch块中抛出。 – 2009-06-30 15:58:33

4

局部变量的作用域被分配给它的块(像Java),但只要块结束就会破坏(不像Java),所以你应该在try块本身做所有你想要的东西(如果你只想处理构造函数的例外,这可能不是所希望的),或者你应该把对象分配给别的地方(例如heap),并在父块中使用一个指针来访问它。

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]); 
} 

而且你需要释放的对象,当你完成你为什么要提到它的try外面使用它

delete input 
+1

我再次看不到指针的内容。 – 2009-06-30 17:53:00

11

块?

而不是

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.... 

最后一个甚至为您提供了分离建筑和使用的好处,它很好地放在单元测试中,您可能希望函数在模拟对象上工作。

+0

另一个很好的答案由你包括几种选择。我喜欢这些:)但为了公平并给出可能的原因,我还经常希望那些“尝试”块中的东西“泄漏”到封闭的范围中。但是这可能是危险的,请考虑以下代码:try {string a;/* < - 假设抛出* /字符串b; } catch(...){} b.size();/* oops,b尚未构建! */ – 2009-06-30 17:16:01

1

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

然后,你可以这样做:

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

根据这可能是比指针搞乱更好的情况。虽然那么你还必须检查创建()做某件事之前实际上叫...

1

我实际上没有看到这里的任何probelms:

一对夫妇的事情,我会更新:

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

所以我会这样做(为了清晰)。

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]); 
    } 
} 
3

可我只是观察到几乎任何最琐碎的构造可以预期抛出异常。因此,在某种意义上,你不应该认为异常是“特殊的”,而是编写你的代码,以便它自然地处理它们。这意味着使用RAII,以及其他在这里回答的其他技术已经提出。

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; 
} 
2

我不能在节目的开头说

VACaptureSource input; 

因为没有默认构造函数。

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

相关问题