5

Nếu tôi xác định operator delete như sau và nếu một ném constructor đối tượng trong biểu thức mới tôi mong đợi để xem kết quả của các cuộc gọi đến các nhà điều hành được xác định xóa:Tại sao chức năng deallocation không được gọi khi constructor đối tượng ném vào một biểu thức mới?

#include <new> 
#include <cstdlib> 
#include <iostream> 

void* 
operator new(std::size_t s){ 
    std::cout << "alloc " << std::endl; 
return std::malloc(s); 
} 

void 
operator delete(void* p) noexcept { 
    std::cout << "dealloc " << std::endl; 
    std::free(p); 
} 
void 
operator delete(void* p,std::size_t) noexcept{ 
    std::free(p); 
    std::cout << "dealloc s" << std::endl; 
} 

struct A{ 
    A(int i){ 
    if(i>0) 
     throw 10; 
    } 
}; 

int main(int argc// will equal 10 
     ,char* arg[]) 
{ 
    for(int i=0;i<argc;++i) 
    auto p=new A{argc}; 
    return 0; 
} 

này chương trình chỉ ra alloc, tại sao các nhà điều hành xóa không được gọi? Trong các tiêu chuẩn [expr.new] nó được xác định rằng:

Nếu bất kỳ phần nào của việc khởi tạo đối tượng mô tả ở trên chấm dứt bằng cách ném một ngoại lệ và một hàm deallocation phù hợp có thể được tìm thấy, chức năng deallocation được gọi đến miễn phí bộ nhớ trong đó đối tượng đang được xây dựng, sau đó ngoại lệ tiếp tục lan truyền trong ngữ cảnh của biểu thức mới.

+0

Bạn không có thử khối catch. – Jarod42

+0

@ Jarod42 Bạn nói đúng! Cảm ơn bạn! Tôi cần bắt khối vì sống chức năng chính với một ngoại lệ là UB hoặc là nó cho một số lý do khác trước? – Oliv

+0

Nó dành cho phần UB. BTW, 'return i;' không hợp lệ ('i' nằm ngoài phạm vi). – Jarod42

Trả lời

1

Nếu bạn khắc phục mã của bạn để ném ngoại lệ, nó hoạt động như mong đợi:

int main(int argc,char* arg[]) 
{ 
    try { 
     new A(2); 
    } 
    catch (...) 
    {} 
} 

Demo

1

Nếu trong quá trình xây dựng sử dụng new, một constructor ném một ngoại lệ, Thư viện C++ runtime:

  1. gọi std::terminate() nếu không tìm thấy trình xử lý bắt phù hợp. Có hay không delete được gọi là triển khai được xác định.

hoặc

  1. cuộc gọi delete cho bạn trước khi "gửi" mà ngoại lệ để xử lý bắt phù hợp nếu ai tìm thấy, mặc dù các destructor không được gọi - tức là ~A(), nếu bạn có, sẽ không được gọi khi số i lớn hơn 1.
+0

Tôi không nghĩ rằng áp dụng ở đây: Nếu bạn bắt ngoại lệ, các nhà điều hành xóa được xác định IS được gọi, trong khi nó không được gọi là nếu không có bắt. Vì vậy, "trước khi đưa ra cho bạn ngoại lệ" là rất mơ hồ. – koalo

+0

@koalo Vâng tôi đã làm một con lợn phải tai của câu trả lời này. Tôi đã sửa đổi. – Bathsheba

2

Như những người khác đã lưu ý, đó là vì bạn không bắt ngoại lệ. Như những nốt nhạc tiêu chuẩn:

C++ 11 §15.3/9:
“ Nếu không xử lý phù hợp được tìm thấy, hàm std::terminate() được gọi; có hay không ngăn xếp được mở trước khi cuộc gọi này đến std::terminate() được thực hiện xác định. ”

Trong khi tôi nghĩ rằng đây không liên quan đặc biệt để ngăn xếp trong trường hợp của bạn, cùng một nguyên tắc sẽ tổ chức ở đây, quá. Vì vậy, nó thực sự là để thực hiện nếu bất kỳ bộ nhớ được làm sạch. Như chúng ta thấy ở đây, nó thường không phải là trường hợp, bởi vì hệ điều hành làm sạch bộ nhớ anyway.

+0

Tôi quan tâm đến bài đăng của bạn bởi vì bạn trả lời cho "tại sao?". Tôi không nghĩ rằng việc dọn dẹp bộ nhớ có liên quan ở đây. Việc triển khai phải đảm bảo rằng các hiệu ứng phụ có thể nhìn thấy thực sự xảy ra (bộ nhớ dọn dẹp có thể không được coi là tác dụng phụ). I/O là một tác dụng phụ có thể nhìn thấy. Vì vậy, Clang và GCC có thể đã thực hiện deallocation bộ nhớ như là một phần của quá trình stack stack, không có gì trong tiêu chuẩn cấm điều này, và trong tài liệu GCC, nó được xác định rằng chấm dứt không thực hiện stack unwinding. Cảm ơn tôi hiểu ngay bây giờ! – Oliv

Các vấn đề liên quan