2009-06-16 31 views
11

Tôi có một lớp giá trị theo mô tả trong "Tiêu chuẩn mã hóa C++", mục 32. Tóm lại, điều đó có nghĩa là nó cung cấp ngữ nghĩa giá trị và không có bất kỳ phương thức ảo nào.Có thể cấm bắt nguồn từ một lớp học tại thời gian biên dịch không?

Tôi không muốn lớp học xuất phát từ lớp học này. Bên cạnh những người khác, một lý do là nó có một destructor không công khai. Nhưng một lớp cơ sở nên có một destructor là public và virtual hoặc protected và nonvirtual.

Tôi không biết khả năng viết lớp giá trị, sao cho nó không thể lấy được từ nó. Tôi muốn cấm nó vào thời gian biên dịch. Có lẽ có bất kỳ thành ngữ nào đã biết để làm điều đó không? Nếu không, có lẽ có một số khả năng mới trong C++ 0x sắp tới? Hay có lý do chính đáng mà không có khả năng như vậy?

Trả lời

6

Ngay cả khi câu hỏi không được đánh dấu cho C++ 11, đối với những người nhận được ở đây, nên được đề cập rằng C++ 11 hỗ trợ định danh theo ngữ cảnh mới final. Xem wiki page

8

Nếu bạn sẵn sàng chỉ cho phép lớp được tạo bằng phương thức nhà máy, bạn có thể có một hàm tạo riêng.

class underivable { 
    underivable() { } 
    underivable(const underivable&); // not implemented 
    underivable& operator=(const underivable&); // not implemented 
public: 
    static underivable create() { return underivable(); } 
}; 
+0

Ah và đó có thể là phương pháp nhà máy tĩnh của lớp học để giữ mọi thứ cùng nhau? Nghe có vẻ tốt đấy. – SebastianK

+3

Không may mắn thay, điều này không dừng lại nguồn gốc.Nó chỉ đơn thuần ngăn cản sự xâm nhập của loại dẫn xuất, ngoại trừ thông qua nhà máy. Sẽ thật tuyệt nếu bạn thực sự có thể tạo ra nguồn gốc, nhưng theo tôi biết, bạn không thể. –

+0

Trong cách thực tế nào có sự khác biệt giữa việc không thể khởi tạo một đối tượng của kiểu dẫn xuất và không thể lấy được ở tất cả (truy cập vào các thành viên tĩnh được bảo vệ?) – Motti

25

Bjarne Stroustrup đã viết về điều này here.


Các bit có liên quan từ liên kết:

Tôi có thể ngăn không cho người bắt nguồn từ lớp học của tôi?

Có, nhưng tại sao bạn muốn? Có hai câu trả lời phổ biến:

  • để biết hiệu quả: để tránh chức năng của tôi gọi là ảo.
  • cho an toàn: để đảm bảo rằng lớp học của tôi không được sử dụng như là một lớp cơ sở (ví dụ, để chắc chắn mà tôi có thể sao chép đối tượng mà không sợ của slicing)

Theo kinh nghiệm của tôi, hiệu quả lý do thường là không đúng chỗ sợ hãi. Trong C++, các lời gọi hàm ảo nhanh đến nỗi việc sử dụng thế giới thực của họ cho một lớp được thiết kế với các hàm ảo không tạo ra chi phí thời gian chạy có thể đo lường được so với các giải pháp thay thế sử dụng các cuộc gọi hàm bình thường. Lưu ý rằng cơ chế gọi hàm ảo thường chỉ được sử dụng khi gọi qua một con trỏ hoặc một tham chiếu. Khi gọi một hàm trực tiếp cho một đối tượng được đặt tên, lớp chức năng ảo trên cao sẽ dễ dàng được tối ưu hóa.

Nếu có nhu cầu chính xác về "giới hạn" một hệ thống phân cấp lớp để tránh các cuộc gọi chức năng ảo, người ta có thể hỏi tại sao những chức năng đó là ảo ngay từ đầu. Tôi đã thấy các ví dụ về các chức năng quan trọng về hiệu suất đã được thực hiện ảo vì không có lý do chính đáng, chỉ vì "đó là cách chúng ta thường làm".

Biến thể khác của vấn đề này, cách ngăn chặn dẫn xuất vì lý do logic, có giải pháp. Thật không may, giải pháp đó không phải là khá. Nó dựa trên thực tế là lớp dẫn xuất nhất trong một hệ thống phân cấp phải xây dựng một cơ sở ảo. Ví dụ:

class Usable; 

class Usable_lock { 
    friend class Usable; 
private: 
    Usable_lock() {} 
    Usable_lock(const Usable_lock&) {} 
}; 

class Usable : public virtual Usable_lock { 
    // ... 
public: 
    Usable(); 
    Usable(char*); 
    // ... 
}; 

Usable a; 

class DD : public Usable { }; 

DD dd; // error: DD::DD() cannot access 
     // Usable_lock::Usable_lock(): private member 

(từ D&E giây 11.4.3).

+0

Rất đẹp, tốt hơn câu trả lời của tôi! – Motti

+2

@Motti: Tôi rất thích câu trả lời của bạn - tôi đã +1 nó. Tùy thuộc vào yêu cầu của mình, nó có thể là một giải pháp tốt hơn nhiều. Bjarnes hơi phức tạp nếu tất cả những gì bạn thực sự muốn làm là ngăn chặn một người nào đó sử dụng loại của họ thay cho bạn. Tôi nghĩ điều quan trọng cần xem xét là tại sao bạn đang cố gắng ngăn chặn sự phát sinh và thực sự đạt được điều gì hữu ích. –

+0

Tại sao lớp cơ sở ảo trong giải pháp này? – SebastianK

2

Vâng, tôi đã gặp phải sự cố tương tự. Điều này được đăng here trên SO. Vấn đề là cách khác xung quanh; tức là chỉ cho phép các lớp đó được bắt nguồn mà bạn cho phép. Kiểm tra xem nó có giải quyết được vấn đề của bạn hay không.

Việc này được thực hiện tại thời gian biên dịch.

4

Chụp ảnh đẹp here.
Nó thực sự thú vị nhưng đó là một hack.
Tự hỏi tại sao stdlib không làm điều này với các thùng chứa của chính nó.

+2

Nó - nó sử dụng thứ hai trong ba phương pháp tiếp cận. –

+0

Tôi thực sự có một lớp kế thừa bản đồ một lần. Nó không cung cấp cho tôi bất kỳ lỗi nào. –

+0

ồ chờ ... không bao giờ. Tôi không nhớ thứ hai là lol. –

0

Giải pháp này không hiệu quả, nhưng tôi để nó làm ví dụ về những việc không nên làm.


Tôi đã không sử dụng C++ trong một thời gian, nhưng theo như tôi nhớ, bạn sẽ có được những gì bạn muốn bằng cách làm cho destructor riêng tư.

CẬP NHẬT:

Trên Visual Studio 2005 bạn sẽ nhận được cảnh báo hoặc lỗi. Kiểm tra các đoạn mã sau:

class A 
{ 
public: 
    A(){} 
private: 
    ~A(){} 
}; 

class B : A 
{ 
}; 

Bây giờ,

B b;

sẽ tạo ra một lỗi "error C2248: 'A :: ~ A': không thể truy cập thành viên tin khai báo trong lớp 'A'"

khi

B *b = new B(); 

sẽ tạo ra cảnh báo "cảnh báo C4624: 'B': destructor không thể được tạo ra bởi vì một destructor lớp cơ sở là không thể tiếp cận ".

Nó trông giống như một nửa solutiom, NHƯNG như orsogufo chỉ, làm như vậy làm cho lớp A không sử dụng được. Rời câu trả lời

+1

Làm điều đó bạn nhận được các đối tượng không thể bị xóa ... đó là sự thật rằng bạn không thể kế thừa từ một lớp như thế, nhưng sau đó làm thế nào để bạn sử dụng nó? –

+0

Chết tiệt, tôi cần phải làm mới C++ của mình. Cảm ơn vì điều đó! – ya23

+0

bạn được chào đón :) cảm ơn bạn đã cập nhật bài đăng. Dù sao, điều tôi muốn nói là bạn không thể khởi tạo một đối tượng thuộc loại A, không chỉ loại B. –

1

tôi nói chung sẽ đạt được điều này như sau:

// This class is *not* suitable for use as a base class 

Các bình luận đi trong tiêu đề và/hoặc trong tài liệu. Nếu khách hàng của lớp bạn không làm theo hướng dẫn trên gói, thì trong C++ họ có thể mong đợi hành vi không xác định. Bắt nguồn mà không được phép chỉ là một trường hợp đặc biệt của việc này. Thay vào đó, họ nên sử dụng bố cục.

Btw, điều này hơi gây nhầm lẫn: "lớp cơ sở phải có trình phá hủy công khai và ảo hoặc được bảo vệ và không ảo".

Điều đó đúng cho các lớp học được sử dụng làm cơ sở cho đa hình thời gian chạy. Nhưng không cần thiết nếu các lớp dẫn xuất không bao giờ được tham chiếu qua con trỏ tới kiểu lớp cơ sở. Nó có thể là hợp lý để có một loại giá trị được sử dụng chỉ cho đa hình tĩnh, ví dụ với mô phỏng năng động ràng buộc. Sự nhầm lẫn là thừa kế có thể được sử dụng cho các mục đích khác nhau trong C++, đòi hỏi sự hỗ trợ khác nhau từ lớp cơ sở. Nó có nghĩa là mặc dù bạn không muốn đa hình động với lớp của bạn, tuy nhiên nó có thể vẫn tốt để tạo ra các lớp dẫn xuất miễn là chúng được sử dụng đúng.

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