2010-10-31 30 views
5

Giả sử tôi xây dựng đối tượng RAII và đối tượng đó có thể không xây dựng được. Làm cách nào để xử lý việc này?Khi đối tượng RAII không thể xây dựng

try { 
    std::vector<int> v(LOTS); 
    // try scope ends here because that's what the catch is for 
} catch(const std::bad_alloc&) { 
    // ... 
} 
// v? what v? 

Đã cấp, hàm tạo mặc định là std::vector sẽ không ném và có thể giúp, nhưng đây không phải là trường hợp chung. Một nhà xây dựng có thể ném rất tốt. Nếu tôi muốn xử lý bất kỳ lỗi mua lại tài nguyên nào, làm cách nào để thực hiện điều đó trong khi vẫn có thể tiếp tục nếu nó không ném?

Chỉnh sửa: Để làm rõ, vấn đề của tôi là nếu tài nguyên không đạt được thì tôi có thể muốn thử lại, v.v. Có lẽ tôi có thể thử mua một nguồn tài nguyên thay thế.

+1

Không chính xác câu hỏi là gì. Mã sử ​​dụng v phải nằm trong khối thử. – Dialecticus

+2

Khi tôi hiểu câu hỏi, vấn đề là để có thể phục hồi từ một ngoại lệ trong hàm tạo 'v',' v' phải được khai báo bên trong phạm vi 'try', có nghĩa là nó không còn có thể là hiển thị * sau * khối 'catch'. Vì vậy, nếu bạn có mã mà một mặt cần có khả năng "bỏ qua" một ngoại lệ khi xây dựng 'v', và mặt khác, có thể sử dụng' v' nếu xây dựng thành công, nó sẽ hơi phức tạp một chút – jalf

+0

Nếu một tài nguyên thất bại để có được sau đó có lẽ tôi có thể làm với một nguồn tài nguyên thay vì, thay vì để cho các ngoại lệ tuyên truyền. Và nếu tài nguyên thứ hai đó thất bại thì có lẽ tôi có một ý tưởng khác, v.v. Tôi cảm thấy như thế này dẫn đến một mớ hỗn độn trong mã không có vấn đề gì tôi tiếp cận. RAII chỉ mô tả làm thế nào để làm sạch trong trường hợp thất bại, không phải làm thế nào để giải quyết thất bại. – wilhelmtell

Trả lời

7

Phụ thuộc ý bạn là "tiến hành". Bất kỳ hoạt động nào đòi hỏi tài nguyên sẽ thất bại: đó là những gì "yêu cầu" có nghĩa là. Vì vậy, khi bạn muốn tiếp tục sau khi một lỗi, bạn có thể kết thúc việc viết mã như thế này:

void something_using_RAII(thingummy &t) { 
    vector<int> v(t.size_required); 
    // do something using v 
} 

... 

for each thingummy { 
    try { 
     something_using_RAII(this_thingummy); 
    } catch(const std::bad_alloc &) { 
     std::cerr << "can't manage that one, sorry\n"; 
    } 
} 

Đó là lý do tại sao bạn chỉ nên bắt ngoại lệ khi có điều gì đó đáng giá bạn có thể làm với họ (trong trường hợp này, suy báo cáo và chuyển sang điều tiếp theo).

Nếu bạn muốn thử một lần nữa trên thất bại, nhưng chỉ khi các nhà xây dựng thất bại, không nếu bất cứ điều gì khác không:

while(not bored of trying) { 
    bool constructor_failed = true; 
    try { 
     vector<int> v(LOTS); 
     constructor_failed = false; 
     // use v 
    } catch(...) { 
     if (!constructor_failed) throw; 
    } 
} 

Đây là nhiều hay ít như thế nào std::new_handler công trình - xử lý được gọi trong điều khoản bắt của một vòng lặp tương tự, mặc dù không cần cờ.

Nếu bạn muốn thử một nguồn tài nguyên khác nhau về thất bại:

try { 
    vector<int> v(LOTS); 
    // use v 
} catch(...) try { 
    otherthing<int> w(LOTS); 
    // use w 
} catch(...) { 
    // failed 
} 

Nếu "sử dụng v" và "sử dụng w" về cơ bản cùng mã, sau đó cấu trúc lại thành một hàm và gọi nó từ cả hai nơi. Chức năng của bạn đang hoạt động khá nhiều vào thời điểm này.

+2

'+ 1' một mình cho câu cuối cùng đó. Không đủ lập trình chú ý đến điều đó. Rất tiếc. – sbi

7

Nếu một hàm tạo RAII ném, tất cả các tài nguyên được liên kết với đối tượng RAII trước điểm ném sẽ được làm sạch đúng cách. Các quy tắc C++ được thiết kế hợp lý để đảm bảo điều đó.

Nếu số máy xây dựng v của bạn ném vì bad_alloc thì bất kỳ đối tượng RAII nào được tạo trước v trong khối try sẽ được dọn dẹp đúng cách.

Vì vậy, nếu bạn sử dụng RAII, bạn không cần hướng dẫn sử dụng try/catch như vậy, vì các đối tượng RAII xử lý việc dọn dẹp cho bạn. Nếu bạn làm cần vì lý do nào đó, trong trường hợp trên, bạn có thể sử dụng hoán đổi như sau.

std::vector<int> v; 
try { 
    std::vector<int> vtry(LOTS); 
    v.swap(vtry); // no-throw 
} catch(const std::bad_alloc&) { 
    // ... 
} 
// v! 
+1

Đây không phải là về việc dọn dẹp. Đó là về việc làm một cái gì đó khi đối tượng RAII không khởi tạo được. Nếu tôi không làm gì thì ngoại lệ sẽ lan truyền. Không tốt. Điều gì sẽ xảy ra nếu chức năng của tôi phải cung cấp bảo đảm không ném? Điều gì nếu đối tượng RAII không có một nhà xây dựng mà sẽ không ném? – wilhelmtell

+0

Bạn cũng có thể nói rằng một đối tượng không có hàm tạo không ném được thiết kế tồi. Đó là tốt. Tôi chưa bao giờ thấy đề xuất này ở bất cứ nơi nào, vì vậy nếu đó là sự thật tôi muốn đưa nó lên bề mặt. Nếu không, hãy nghe cách bạn xử lý tình huống khi bất kỳ ctor nào có thể ném. – wilhelmtell

+0

@wilhelmtell, "Điều gì nếu đối tượng RAII không có một nhà xây dựng mà sẽ không ném?" -> Tôi nghĩ rằng bạn cần phải đặt toàn bộ mã trong mệnh đề 'try' sau đó. Tôi không nghĩ rằng đó là thiết kế xấu để không có một nhà xây dựng không ném. Đôi khi nó có thể không phù hợp. Nhưng tôi thấy nó hữu ích nếu như một trạng thái no-throw tồn tại. Bạn cũng có thể bọc đối tượng của mình vào một con trỏ thông minh có trạng thái mặc định không rõ ràng (như 'shared_ptr'). Nhưng tôi nghĩ điều đó thật kinh tởm. Hãy thử các khối có chi phí thực hiện bằng không trong C++, nếu được thực hiện đúng với các bảng. –

2

Nếu không thể tạo v, tất cả mã cố gắng sử dụng v không thể thực hiện được. Di chuyển mã catch sau mã mà mã sử dụng v, ở nơi hợp lý để tiếp tục thực hiện nếu không có v.

+2

Vấn đề, như tôi hiểu nó, là sau đó bạn không thể phân biệt trong đánh bắt từ một ngoại lệ được ném bởi c's vtor hoặc bởi mã khác trong khối try. –

+0

@Roger: trong trường hợp này (chặn các giải pháp của litb, mà là tốt nhưng dựa vào 'trao đổi', mà không phải tất cả các lớp RAII có) bạn phải viết một cái gì đó gây phiền nhiễu như' try {vector v (LOTS); bool flag = true; thử {sử dụng v; flag = false; } catch (...) {X}} catch (...) {Y} '. X xử lý các ngoại lệ từ mã khác trong khối try, Y xử lý các ngoại lệ từ constructor * hoặc destructor * của 'v', và từ X. Nhưng destructor của' v' không nên ném và các ngoại lệ từ X có thể được nhận diện bởi lá cờ. –

0

Tất cả mã sử dụng v cần phải nằm trong khối thử.Nếu câu hỏi là làm cách nào để thu hẹp mã đã ném ngoại lệ, bạn có thể sử dụng một số loại cờ để chỉ ra vị trí trong khối thử, như sau:

string flag; 
try 
{ 
    flag = "creating vector<int> v"; 
    std::vector<int> v(LOTS); 

    flag = "performing blaggity bloop"; 
    blaggity_bloop(); 

    flag = "doing some other stuff"; 
    some_other_stuff(); 
} 
catch(const std::bad_alloc&) 
{ 
    cerr << "Bad allocation while " << flag << endl; 
} 
Các vấn đề liên quan