2010-12-12 9 views
5

Tôi nhận ra điều này là thiếu khôn ngoan, và tôi không đề xuất để làm điều đó, nhưng tôi tò mò về việc liệu sau đây là thực sự chính thức bất hợp pháp:Có phải mô phỏng bằng tay hiệu ứng của toán tử xóa trong C++ một cách chính thức bất hợp pháp không?

#include <iostream> 

struct X 
{ 
    ~X() 
    { 
     std::cout << "~X()\n"; 
    } 
}; 

int main() 
{ 
    X *x = new X; 
    //delete x; 
    x->~X(); 
    ::operator delete(x); 
    return 0; 
} 

Đó là sự hiểu biết của tôi rằng delete x; tương đương với gọi destructor và sau đó gọi ::operator delete(x);, nhưng nó là hợp pháp đối với tôi để làm điều đó theo cách thủ công theo tiêu chuẩn? Tôi biết đây là một điều hợp lệ khi sử dụng vị trí mới, nhưng trong trường hợp không phải là vị trí thì sao? Linh cảm của tôi là nó có thể là bất hợp pháp vì delete (và không phải operator delete) phải được thực hiện cho mỗi new, nhưng tôi muốn được biết chắc chắn.

+0

bạn đang hỏi liệu 'xóa x;' có tương đương với cách gọi hàm hủy và sau đó 'toán tử xóa (x);' cho * mẫu mã cụ thể này * hay * nói chung *? –

+0

@Ben: Trong mẫu mã cụ thể này - nhưng tôi cũng quan tâm đến trường hợp chung (mà bây giờ tôi giả định câu trả lời là không). –

+0

Rất vui khi tôi hỏi câu hỏi này - có rất nhiều câu trả lời hơn tôi tưởng tượng. –

Trả lời

3

Tôi khá chắc chắn rằng điều này không tuân thủ tiêu chuẩn. Không nơi nào trong tiêu chuẩn (mà tôi biết) nó nói rằng toán hạng của delete p; được chuyển trực tiếp đến chức năng deallocation, và cho delete [] nó gần như chắc chắn không phải là được chuyển qua không thay đổi.

Nó có thể sẽ hoạt động trên tất cả các triển khai thực tế, tuy nhiên.

Để chắc chắn, bạn không nên gọi chức năng deallocation toàn cầu một cách rõ ràng. Tốt trong ví dụ của bạn, không có các hàm phân bổ và phân bổ người dùng xác định, nhưng không phải trong trường hợp chung (thực ra, có cú pháp nào để gọi hàm deallocation theo lớp cụ thể nếu nó tồn tại hay không?).

Ngoài ra, trong trường hợp chung, con trỏ được chuyển đến hàm deallocation chắc chắn không phải là toán hạng xóa. Xem xét:

struct A 
{ 
    virtual ~A() {} 
}; 

struct B1 : virtual A {}; 

struct B2 : virtual A {}; 

struct B3 : virtual A {}; 

struct D : virtual B1, virtual B2, virtual B3 {}; 

struct E : virtual B1, virtual D {}; 

int main(void) 
{ 
    B3* p = new D(); 
    p->~B3(); // should be ok, calling virtually 
    operator delete(p); // definitely NOT OK 

    return 0; 
} 
+0

+1, tôi nghĩ rằng tôi chắc chắn đồng ý về khía cạnh "không nên" của tất cả :) Những kẻ trong comp.lang.C++ cũng đã đề cập rằng người dùng định nghĩa 'toán tử new' và' toán tử delete' sẽ gây ra vấn đề, vì vậy nó có vẻ như một điều ngu ngốc để làm ngay cả khi nó là hợp pháp. Đối với câu hỏi cú pháp - tôi biết bạn có thể gọi 'T :: toán tử delete' để có được một lớp cụ thể của T cung cấp nó tồn tại. Có thể có một số mẫu siêu lập trình mà người ta có thể làm để đảm bảo cái toàn cầu được gọi là khác, nhưng tôi không phải là một chuyên gia về điều đó. Nó có thể là một thứ của SFINAE nếu tôi có thể đoán được. –

+1

"_ Tôi khá chắc chắn rằng điều này không tuân thủ tiêu chuẩn._" Bạn có nghĩa là mã ** cụ thể ** trong câu hỏi không được bảo đảm để hoạt động không? Bạn có nghĩa là 'new X' có thể không gọi 'toán tử new (sizeof (X))'? – curiousguy

+0

@curiousguy; Tôi có nghĩa là 'mới X' được phép có các tác dụng phụ khác ngoài việc gọi hàm phân bổ và hàm tạo, mà yêu cầu một biểu thức xóa phù hợp sau này. –

2

Tôi tin rằng bạn không gọi hành vi không xác định nếu bạn gọi hàm hủy và giải phóng bộ nhớ.

+0

Cảm ơn - nó không phải là câu trả lời cuối cùng tôi đang tìm kiếm, nhưng tôi đánh giá cao ý kiến. Tôi đã hỏi điều này trên comp.lang.C++ trong trường hợp bất cứ ai có thể khai sáng cho tôi với chương & câu từ tiêu chuẩn. –

+1

Gọi cho trình phá hủy và giải phóng bộ nhớ, vâng. Nhưng bạn phải giải phóng bộ nhớ phù hợp bằng cách gọi đúng chức năng deallocation * và * truyền con trỏ bên phải. Mà là một LOT phức tạp hơn trong trường hợp chung, hơn mã trong câu hỏi này cho thấy. –

0

Tôi nghĩ rằng

::operator delete(x); 

cũng giống như

delete x; 

nếu phương pháp delete không ghi đè bởi X?

+2

Nó không phải. Toán tử 'delete' gọi hàm hủy, nếu con trỏ không phải là null, và sau đó gọi hàm deallocation, nếu con trỏ không phải là null và thậm chí nếu nó là. Tham số được truyền cho hàm deallocation không được chỉ định trong tiêu chuẩn. –

3

Nếu bạn đang đi theo cách này, bạn nên thay thế

X *x = new X; 

bởi

X* x = static_cast<X*>(::operator new(sizeof(X))); 

try { new(x) X; } 
catch (...) { ::operator delete(x); throw; } 

mà nên (hãy sửa lại cho tôi nếu tôi sai) nhất quán với bạn phương pháp tiêu hủy và tương đương khá nhiều chức năng với new.

+0

Cảm ơn - đó là sử dụng vị trí mới mặc dù (tôi biết đó là hợp lệ). Tôi tò mò hơn là liệu có bất hợp pháp để trộn lẫn và kết hợp cả hai. –

+0

Tôi khá chắc chắn đây là chính xác những gì 'mới' không - trừ khi có một số' mới' quá tải xảy ra. – curiousguy

1

@StuartGolodetz

trả lời, trả lời bình luận

Có thể có một số mẫu Lập trình meta ai có thể làm gì để đảm bảo một thế giới được gọi là khác, nhưng tôi không một chuyên gia về điều đó. Nó có thể là một thứ của SFINAE nếu tôi có thể đoán được.

 
struct B { 
    virtual ~B(); 
}; 

struct D : B { 
    operator new (size_t); 
    operator delete (void*); 
}; 

B *p = new D; 

decltype(typeid(*p))::operator delete (...); // hypothetical C++++ 

Meta this!

+0

Điều này sử dụng loại tĩnh '* p', là' B', phải không? Và do đó gọi sai deallocator? Trong thực tế nó thậm chí không nên biên dịch, bởi vì 'B' không có thành viên' toán tử delete'. –

+0

@BenVoigt Hum, bạn đã lấy nó theo nghĩa đen? '* p ::' thậm chí không phải là C++! – curiousguy

+0

Tôi cho là không, nhưng 'decltype (* p) ::' là. –

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