2009-06-30 46 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塊,並在其中使用它。

相關問題