2015-04-13 16 views
10

Nếu tôi biên dịch và chạy như sau:Tại sao không bảo vệ C++ - Cli destructors gây ra lỗi biên dịch?

using namespace System; 

ref class C1 
{ 
public: 
    C1() 
    { 
     Console::WriteLine(L"Creating C1"); 
    } 

protected: 
    ~C1() 
    { 
     Console::WriteLine(L"Destroying C1"); 
    } 
}; 

int main(array<System::String ^> ^args) 
{ 

    C1^ c1 = gcnew C1(); 
    delete c1; 

    return 0; 
} 

... mã biên dịch mà không có lỗi và chạy cho tôi này:

Creating C1 
Destroying C1 
Press any key to continue . . . 

Nếu tôi làm như vậy trong C++ Tôi nhận được một lỗi dọc theo các dòng sau:

1>ProtectedDestructor.cpp(45): error C2248: 'C1::~C1' : cannot access protected member declared in class 'C1' 
1>   ProtectedDestructor.cpp(35) : compiler has generated 'C1::~C1' here 
1>   ProtectedDestructor.cpp(23) : see declaration of 'C1' 

... vậy tại sao nó hợp lệ trong CLI?

Trả lời

14

Đây là sự cố trừu tượng bị rò rỉ . C++/CLI có một vài trong số chúng, chúng tôi đã trải qua vấn đề từ khóa const. Nhiều giống nhau ở đây, thời gian chạy không có bất kỳ khái niệm nào của một destructor, chỉ có finalizer là có thật. Vì vậy, nó phải được giả mạo. Nó là khá quan trọng để tạo ra ảo tưởng đó, mô hình RAII trong bản địa C++ là thánh thiện.

Đó là giả mạo bằng cách bắt chước khái niệm của một destructor trên đầu trang của giao diện IDisposable. Một trong đó làm cho phá hủy xác định làm việc trong .NET. Rất phổ biến, ví dụ: sử dụng từ khóa trong ngôn ngữ C# gọi ngôn ngữ đó. Không có từ khóa như vậy trong C++/CLI, bạn sử dụng toán tử delete. Cũng giống như bạn sẽ ở bản địa C++. Và trình biên dịch giúp, tự động phát ra các cuộc gọi destructor khi bạn sử dụng ngữ nghĩa stack. Cũng giống như trình biên dịch C++ bản địa. Giải cứu RAII.

Trừu tượng rõ ràng, nhưng có, nó bị rò rỉ. Vấn đề là phương thức giao diện luôn công khai. Đó là về mặt kỹ thuật có thể làm cho nó riêng với thực hiện giao diện rõ ràng mặc dù nó chỉ là một stopgap:

public ref class Foo : IDisposable { 
protected: 
    //~Foo() {} 
    virtual void Dispose() = IDisposable::Dispose {} 
}; 

Tạo một danh sách lỗi rất ấn tượng khi bạn cố gắng này, trình biên dịch chống lại khó như nó có thể :). C2605 là cái duy nhất có liên quan: "'Vứt bỏ": phương pháp này được dành riêng trong một lớp được quản lý ". Nó không thể duy trì ảo giác khi bạn làm điều này.

Ngắn câu chuyện ngắn, việc triển khai phương thức IDisposable :: Dispose() luôn công khai, bất kể khả năng truy cập của trình phá hủy. Toán tử delete gọi nó. Không giải quyết vấn đề này.

5

Ngoài câu trả lời chi tiết của Hans rằng delete trên một đối tượng C++/CLI thực sự là kích hoạt giao diện IDisposable, và giao diện người thừa kế luôn công khai , nó có thể là tốt đẹp để hỏi

như thế nào các destructor được bảo vệ được gọi là, sau đó?

Phương thức do biên dịch viên tạo ra gọi là destructor do người dùng xác định. Bởi vì phương pháp Dispose này là một thành viên của lớp, nó có quyền truy cập vào các thành viên lớp protectedprivate, chẳng hạn như trình phá hủy.

(Trong C++ gốc, trình biên dịch không phải tuân theo quy tắc trợ năng, vì nó là trình biên dịch thực thi chúng. Trong .NET, trình xác minh IL thực thi chúng.)


Trên thực tế, các trung tâm giải thích của ông trên thực tế là trình biên dịch không cho phép thực hiện rõ ràng của IDisposable::Dispose(), trong trường hợp nó có thể là một thành viên tư nhân. Nhưng điều đó hoàn toàn không liên quan. virtual thành viên có thể đạt được thông qua loại khai báo. Và delete không gọi số object->Dispose(), gọi số safe_cast<IDisposable^>(object)->Dispose().

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