2013-07-29 39 views
6

Tôi xin lỗi nếu câu hỏi này là trùng lặp - Tôi đã tìm kiếm trong một thời gian, nhưng có thể là Google-fu của tôi không phải chỉ để ngửi.Làm thế nào bộ nhớ nên được giải phóng sau khi một ngoại lệ được ném vào C + +?

Tôi đang sửa đổi chương trình C++ gọi vào thư viện C. Thư viện C phân bổ một bộ nhớ (sử dụng malloc()), và chương trình C++ sử dụng nó và sau đó giải phóng nó. Việc nắm bắt là chương trình C++ có thể ném một ngoại lệ giữa chừng thực thi, khiến bộ nhớ được cấp phát không bao giờ được giải phóng.

Là một (chứ không phải giả tạo) ví dụ:

/* old_library.c */ 
char *allocate_lots() { 
    char *mem = (char *)malloc(1024); 
    return mem; 
} 

/* my_prog.cpp */ 
void my_class::my_func() { 
    char *mem = allocate_lots(); 
    bool problem = use(mem); 
    if (problem) 
     throw my_exception("Oh noes! This will be caught higher up"); 
    free(mem); // Never gets called if problem is true 
} 

Câu hỏi của tôi là: làm thế nào ought tôi để đối phó với điều này? Ý tưởng đầu tiên của tôi là bọc toàn bộ thứ trong một khối try/catch, và trong phần đánh bắt, chỉ cần kiểm tra và giải phóng bộ nhớ và vứt bỏ ngoại lệ, nhưng điều này dường như không có vẻ và clunky với tôi (và sẽ không hoạt động tốt nếu tôi muốn thực sự bắt ngoại lệ). Có cách nào tốt hơn để làm điều đó không?

EDIT: Tôi có lẽ đã đề cập rằng chúng tôi đang sử dụng g ++ 4.2.2, từ năm 2007 trước khi std :: unique_ptr được giới thiệu. Phấn nó lên quán tính của công ty.

+1

Tại sao bạn không thể giải phóng bộ nhớ trước khi ném ngoại lệ? –

+13

Sử dụng RAII, sự cố được giải quyết. – Borgleader

Trả lời

7

Sử dụng std::unique_ptr với một deleter tùy chỉnh mà các cuộc gọi miễn phí:

class free_mem { 
public: 
    void operator()(char *mem) { free(mem); } 
}; 

void my_class::my_func() { 
    std::unique_ptr<char, free_mem> mem = allocate_lots(); 
+0

Điều này có thể là câu trả lời đúng, ngoại trừ việc chúng tôi đang sử dụng phiên bản cũ của g ++ (xem phần chỉnh sửa ở trên). – Dan

+6

Viết thay thế unique_ptr của riêng bạn, sau đó giải thích cho sếp của bạn tại sao bạn dành 2 ngày gỡ lỗi một công cụ ngôn ngữ cơ bản mà bạn sẽ nhận được miễn phí nếu họ cho phép nâng cấp. Lặp lại nó cho đến khi chúng bắt đầu giảm thời gian lãng phí với các công cụ lỗi thời so với thời gian nâng cấp. – DanielKO

4

Bạn nên chắc chắn rằng bạn không ném cho đến khi bạn đã giải phóng bộ nhớ - hoặc là bạn sử dụng một cấu trúc con trỏ thông minh thích hợp để lưu trữ các mem, như vậy mà khi throw xảy ra, và ngăn xếp tháo, các mem được giải thoát.

1

Có lý do nào không đơn giản giải phóng bộ nhớ bên trong mệnh đề if không?

if (problem) { 
    free (mem); 
    throw my_exception ("Drat!"); 
} 
+6

Sao chép mã, mặc dù trong trường hợp cụ thể này, 'miễn phí' thực sự có thể được di chuyển trước khi có điều kiện hoàn toàn. Dài hạn, có một mối nguy hiểm mà ai đó không biết về những gì đang xảy ra ở đây có thể chèn một 'ném' hoặc' trả lại' khác hoặc gọi đến một hàm khác có thể ném mà không cần xử lý tài nguyên mong manh. RAII bảo vệ chống lại người đàn ông tiếp theo là ngu ngốc. – Casey

4

Bọc rằng Rascal:

struct malloc_deleter { 
    template <typename T> 
    void operator() (T* p) const { 
    free(p); 
    } 
}; 

void my_class::my_func() { 
    std::unique_ptr<char[],malloc_deleter> mem{allocate_lots()}; 
    bool problem = use(mem.get()); 
    if (problem) 
     throw my_exception("Oh noes! This will be caught higher up"); 
} 
+0

Mmm. Bạn có thể giải thích việc sử dụng 'char []'? – sehe

+0

'unique_ptr ' là một chuyên môn hóa một phần mà quá tải toán tử '[]' thay vì 'toán tử ->' và sử dụng 'delete []' theo mặc định (không quan trọng ở đây). – Casey

+0

Tôi luôn luôn nhầm lẫn với điều đó, sau đó. Tôi nghĩ rằng 'shared_ptr' đã có điều đó. Nhưng rõ ràng, họ quên thêm tương tự cho 'shared_ptr' – sehe

1

Sử dụng unique_ptr: http://coliru.stacked-crooked.com/view?id=cd3f0fc64d99cc07a2350e2ff9686500-542192d2d8aca3c820c7acc656fa0c68

#include <stdexcept> 
#include <iostream> 

#include <memory> 

/* old_library.c */ 
char *allocate_lots() 
{ 
    return static_cast<char*>(malloc(1024)); 
} 

struct my_exception : virtual std::exception { 
    const char* const msg; 
    my_exception(const char* const msg) : msg(msg) {} 
    const char* what() const noexcept { return msg; } 
}; 

struct my_class 
{ 
    struct Free { void operator() (char* p) const { free(p); } }; 
    /* my_prog.cpp */ 
    void my_func() 
    { 
     std::unique_ptr<char, Free> mem; 

     mem.reset(allocate_lots()); 
     bool problem = use(mem.get()); 

     if(problem) 
     { 
      throw my_exception("Oh noes! This will be caught higher up"); 
     } 
    } 

    static bool use(char*) { return true; } 
}; 

int main() 
{ 
    my_class prog; 
    prog.my_func(); 
} 
+2

@Dan thay thế 'unique_ptr' bằng [' boost :: scoped_ptr'] (http://www.boost.org/doc/libs/1_54_0/libs/smart_ptr/scoped_ptr.htm) nếu bạn không có nó – sehe

2

Kể từ khi y ạn sử dụng phiên bản trình biên dịch cũ mà không có unique_ptr, bạn có thể viết RAII của bạn bao bọc bản thân:

class ResourceWrapper { 
public: 
    ResourceWrapper(char* ptr) : m_ptr(ptr) {} 
    ~ResourceWrapper() { free(m_ptr); } 
    // whatever getters suit you, at the very least: 
    char* get() const { return m_ptr; } 
private: 
    char* const m_ptr; 
}; 

void my_class::my_func() { 
    ResourceWrapper mem(allocate_lots()); 
    bool problem = use(mem.get()); 
    if (problem) 
     throw my_exception("Oh noes! This will be caught higher up"); 
} 

Chỉ cần chắc chắn không cho phép sao chép/chuyển nhượng thậm chí ngầm (đó là lý do tôi đã m_ptr const) hoặc bạn có nguy cơ kết thúc với việc giải phóng đôi bộ nhớ của bạn (ngữ nghĩa "di chuyển" à la auto_ptr là tốt nhất nên tránh trừ khi bạn hoàn toàn cần nó).

2

Vì bạn không thể sử dụng std::unique_ptr, bạn có thể tạo lớp deleter của riêng bạn để kiểm soát thời gian tồn tại của con trỏ theo kiểu RAII. Để giữ cho nó đơn giản ví dụ này không quấn con trỏ thực tế nhưng tồn tại bên cạnh nó; một cách tiếp cận an toàn hơn là tạo một lớp con trỏ thông minh thực sự.

class AutoFree 
{ 
public: 
    AutoFree(void* p) : m_p(p) 
    { 
    } 
    ~AutoFree() 
    { 
     free(m_p); 
    } 
private: 
    void* m_p; 
}; 

void my_class::my_func() { 
    char *mem = allocate_lots(); 
    AutoFree mem_free(mem); 
    bool problem = use(mem); 
    if (problem) 
     throw my_exception("Oh noes! This will be caught higher up"); 
} 
+0

Được hiển thị trong hành động: http://ideone.com/iLgXdY –