2011-08-10 37 views
10

Ví dụ dưới đây minh họa cách ngăn sao chép lớp dẫn xuất. Nó dựa trên một lớp cơ sở mà cả nhà xây dựng bản sao và toán tử gán bản sao được khai báo private.Khi nào và tại sao nên hủy trong lớp cơ sở KHÔNG được định nghĩa là ảo?

class Uncopyable 
{ 
protected: 
    // allow construction and destruction of derived objects... 
    Uncopyable() {} 
    ~Uncopyable() {} 

private: 
    // but prevent copying... 
    Uncopyable(const Uncopyable&); 
    Uncopyable& operator=(const Uncopyable&); 
}; 

Chúng ta có thể sử dụng lớp này, kết hợp với thừa kế riêng, để làm cho các lớp học uncopyable:

class derived: private Uncopyable 
{...}; 

Chú ý rằng các destructor trong lớp Uncopyable không khai báo là virtual.

Trước đây, tôi đã học rằng

  • Cấu trúc trong lớp cơ sở shoul d là virtual.
  • Cấu trúc trong lớp không phải cơ sở không được thực hiện virtual.

Trong ví dụ này, hàm hủy cho Uncopyable không phải là virtual, nhưng nó đang được kế thừa từ đó. Điều này dường như đi ngược lại sự khôn ngoan mà tôi đã học được từ trước.

Khi nào và tại sao trình phá hủy trong lớp cơ sở KHÔNG được định nghĩa là virtual?

+0

Bạn đã thực hiện Uncopyable. Bạn đã không làm cho Uninheritable. –

+1

Giảm âm lượng trên ** HUGE BOLD ** văn bản, phải không? Nó làm cho nó khó chịu để đọc! – Praetorian

+0

@Praetorian, Văn bản in đậm giúp sắp xếp mục của bạn rõ ràng và đặt câu hỏi/câu trả lời của bạn dễ đọc hơn, (trừ khi bạn đang sử dụng điện thoại di động mà tôi đoán?). Kiểm tra ở đây: http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three –

Trả lời

12

Trình hủy lớp cơ sở chỉ cần là virtual nếu bạn có thể thử deallocating một đối tượng của một loại có nguồn gốc thông qua một con trỏ của loại cơ sở. Do đó, nếu bạn chỉ được kế thừa từ lớp cơ sở riêng thay vì công khai, như trường hợp trong Uncopyable, thì bạn không cần phải lo lắng về việc đặt một hàm hủy virtual, bởi vì khi sử dụng quyền thừa kế riêng tư, bạn có thể ' t lấy một con trỏ đến đối tượng có nguồn gốc và lưu nó vào một con trỏ tới kiểu cơ sở.

Một ví dụ khác có thể là nếu bạn đã sử dụng một lớp mixin như thế này mà làm cho một lớp theo dõi số lượng phân bổ đối tượng, nơi mixin được thừa hưởng từ để có được hành vi nhưng không được đối xử polymorphically:

template <typename T> class Counter { 
public: 
    Counter() { ++numInstances; } 
    Counter(const Counter&) { ++numInstances); 
    ~Counter() { --numInstances; } 

    static unsigned getNumInstances() { return numInstances; } 

private: 
    static unsigned numInstances; 
} 
template <typename T> unsigned Counter<T>::numInstances = 0; 

Nói chung, khi sử dụng đa hình tĩnh, bạn không cần trình phá hủy ảo vì bạn không bao giờ xử lý các lớp đa hình bằng cách sử dụng con trỏ tới loại cơ sở. Bạn chỉ sử dụng một con trỏ đến kiểu có nguồn gốc. Có lẽ có một vài trường hợp khác mà tôi không đề cập đến ở đây, nhưng hai trường hợp này (các thừa kế riêng, các lớp mixin và đa hình tĩnh) bao gồm nhiều không gian mà các trình phá hủy ảo không được yêu cầu.

+0

Phần thú vị là tăng không khuyến khích sự thừa kế riêng tư từ 'không thể đọc được'. Mặt khác, sử dụng 'noncopyable & 'hoặc' noncopyable * 'có vẻ vô nghĩa đến mức chắc chắn không ai sẽ làm điều đó, đúng không? – pmr

+0

Tôi chưa từng thấy ai cố gắng làm điều này ... Tôi không biết tại sao bạn lại làm thế! – templatetypedef

+0

Một lý do khác để không làm cho destructor ảo trong trường hợp cụ thể này ('Uncopyable' hoặc' boost :: noncopyable') là không áp đặt vtable trên mọi class muốn sử dụng nó. – Praetorian

0

Khi bạn cần các đối tượng của mình là dữ liệu cũ thuần túy, không có vtable. Tôi muốn bình luận ra khỏi nó nếu tôi cần nó, vì 99% thời gian rời khỏi 'ảo' trong destructors lớp cơ sở chỉ đơn giản là một sai lầm mà ai đó sẽ muốn sửa chữa.

+0

Khi bạn cần các đối tượng của mình là dữ liệu cũ, bạn thường không sử dụng thừa kế. (Cụ thể, vì bất kỳ lớp nào có cơ sở không phải là loại POD) –

2

Khi bạn thiết kế cơ sở không phải là giao diện, nhưng như thực hiện chi tiết (chú ý private thừa kế từ Uncopyable).

1

Bạn về mặt kỹ thuật không phải làm cho trình phân giải ảo của bạn ảo nếu bạn biết sẽ không có ai xóa nó dưới dạng Không thể sao chép *, nhưng sẽ luôn xóa nó dưới dạng phân lớp giống nhau.

Có điều này về cơ bản, những gì @templatetypedef nói, nhưng tôi sẽ giải thích nó theo một cách dễ dàng hơn.

Vì vậy: nếu mọi người có thể làm điều gì đó như thế này:

void aFunction(Uncopyable* obj) { 
    delete obj; 
} 

Sau đó, bạn nên tuyên bố destructor của bạn ảo (để đảm bảo lớp con tiềm năng có được destructor của họ được gọi là

Tuy nhiên, nếu bạn biết. mọi người sẽ xóa các Lớp con như vậy:

class Widget : public Uncopyable 
{ 
    .... 
}; 

void aFunction(Widget* obj) { 
    delete obj; 
} 

Sau đó, bạn không phải thực hiện việc hủy tor ảo (như subclasses destructor sẽ được gọi).

Ít nhất, đó là sự hiểu biết của tôi.

0

Quy tắc chung là làm cho công cụ hủy của bạn công khai và ảo, hoặc được bảo vệ và không phải ảo. Trong trường hợp đầu tiên, đối tượng của bạn sử dụng khả năng phá hủy đa hình và phá hủy ảo sẽ làm điều đúng. Trong trường hợp thứ hai nó sẽ chỉ bị phá hủy như lớp con thực tế và vẫn làm điều đúng.

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