2010-06-22 49 views
7

Tôi biết có các phương pháp để ngăn không cho lớp được tạo trên heap, bằng cách ngăn người dùng sử dụng toán tử newdelete. Tôi đang cố gắng làm điều ngược lại. Tôi có một lớp mà tôi muốn ngăn chặn người dùng tạo một thể hiện của nó trên ngăn xếp, và chỉ các trường hợp được xúi giục bằng toán tử new sẽ biên dịch. Cụ thể hơn, tôi muốn đoạn mã sau để nhận được một lỗi trong quá trình biên soạn:C++, ngăn chặn cá thể lớp được tạo trên ngăn xếp (trong quá trình biên dịch)

MyClass c1; //compilation error 

MyClass* c1 = new MyClass(); //compiles okay 

Từ tìm kiếm trên web, tôi thấy đề nghị này về làm thế nào để làm điều đó:

class MyClass { 
public: 
    MyClass(); 
private: 
    void destroy() const { delete this; } 

... 

private: 
    ~MyClass(); 
}; 

int main(int argc,char** argv) 
{ 
    MyClass myclass; // <--- error, private destructor called here !!! 

    MyClass* myclass_ptr = new MyClass; 
    myclass_ptr->destroy(); 
} 

Những gì tôi không hiểu là lý do tại sao điều này sẽ hoạt động. Tại sao destructor được gọi trong khi tạo một thể hiện của MyClass?

+4

bằng cách gọi hàm hủy() như thế này sẽ cung cấp cho bạn lỗi trình biên dịch vì nó được khai báo riêng –

+3

Tôi muốn biết lý do bạn muốn điều này? – log0

+1

xem câu hỏi khác của tôi, giải thích rõ ràng: http: // stackoverflow.com/questions/3095856/prevent-unaligned-data-on-the-heap – eladidan

Trả lời

21

Khi myclass kết thúc phạm vi của nó (} tiếp theo) trình biên dịch gọi hàm hủy để giải phóng nó khỏi ngăn xếp. Tuy nhiên, nếu destructor là private, thì destructor không thể được truy cập, vì vậy lớp không thể được đặt trên stack.

Tôi không thích giao diện của delete this. Nói chung tôi nghĩ rằng các đồ vật không nên tự hủy diệt. Có lẽ cách tốt hơn là có một hàm tạo riêng cho lớp của bạn, sau đó sử dụng hàm tĩnh để tạo một cá thể.

// In class declaration... 
static MyClass* Create() 
{ 
    return new MyClass(); // can access private constructor 
} 

// ... 

MyClass myclass; // illegal, cannot access private constructor 

MyClass* pMyClass = MyClass::Create(); 
delete pMyClass; // after usage 
+1

+1 - thay vì lộn xộn với destructors tư nhân bằng cách sử dụng một biến thể của mô hình singleton là cách để đi –

+15

@ Holger: Mục đích duy nhất của một singleton là cho phép _only one_ instance, và điều này không làm điều này, vì vậy __it không phải là một singleton nào cả .__ _ Tôi ước tôi có thể bỏ phiếu bình chọn._ – sbi

+0

+1 Cách tốt nhất, sử dụng phương pháp nhà máy thay vì hack thứ gì đó có thể không rõ ràng đối với ai đó phải sử dụng mã của bạn. – fingerprint211b

12

Tại sao trình hủy được gọi trong khi tạo một phiên bản MyClass?

Nó không phải. Nó phải được gọi tự động khi cá thể đó đi ra ngoài phạm vi. Nếu nó là riêng tư, trình biên dịch không được tạo ra mã đó, do đó có lỗi.

Nếu bạn nghĩ rằng làm cho tin destructor là tối nghĩa, một cách khác để hạn chế một lớp phân bổ động là làm cho tất cả các nhà thầu tư nhân và chỉ có MyClass::create() chức năng trở về đối tượng được cấp phát động:

class MyClass { 
public: 
    static MyClass* create()    {return new MyClass();} 
    static MyClass* create(const Foo& f) {return new MyClass(f);} 
private: 
    MyClass(); 
    MyClass(const Foo&); 
}; 

Lưu ý rằng trở về con trỏ khỏa thân với các đối tượng cần xóa sẽ bị cau mày. Thay vào đó, bạn nên trả lại con trỏ thông minh:

class MyClass { 
public: 
    static std::shared_ptr<MyClass> create()    {return new MyClass();} 
    static std::shared_ptr<MyClass> create(const Foo& f) {return new MyClass(f);} 
    // ... 
}; 
+1

-1 cho "Nếu riêng tư, trình biên dịch không được tạo mã đó, ..." – berkus

+0

@Berkus: Xin lỗi tôi? – sbi

+0

Nếu nó là riêng tư, nó có thể được gọi từ bên trong phạm vi lớp học. Nó không có nghĩa là trình biên dịch phải hoặc không được tạo mã. – berkus

1

Vì khi trường hợp vượt quá phạm vi, nó phải bị hủy bằng cách sử dụng hàm hủy. Con trỏ ví dụ không làm điều này.

1

Bất cứ khi nào biến cục bộ nằm ngoài phạm vi, biến đó sẽ bị hủy. Và trên sự hủy diệt, phá hủy đối tượng được gọi. Ở đây, phạm vi là chức năng chính. Khi thoát chương trình, destructor của đối tượng myclass sẽ được gọi là

1

Nó không phải. Trình biên dịch đang cố gắng gọi hàm hủy khi nó nằm ngoài phạm vi và chỉ ra dòng mã tạo ra hiệu ứng này, điều này hữu ích hơn nhiều so với việc trỏ vào cuối hàm.

+0

Điều này nên là một bình luận (bạn không trả lời (chính) câu hỏi ở tất cả). Tương tự cho Pardeep ở trên. – n1ckp

+1

Câu hỏi là "Tại sao destructor được gọi trong khi tạo ra một thể hiện của MyClass?". Đây là câu trả lời cho câu hỏi đó. Học đọc trước. – berkus

+1

@Berkus huh? Câu hỏi trong tiêu đề là "C++, ngăn chặn cá thể lớp được tạo ra trên ngăn xếp (trong quá trình biên dịch)". Xin lỗi nhưng điều này KHÔNG trả lời câu hỏi đó. – n1ckp

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