2012-11-06 31 views
10

Có bằng cách nào đó có thể, để thực hiện những điều sau:Ẩn thực hiện bằng cách sử dụng một con trỏ (Pimpl thành ngữ)

x.hpp - file này được bao gồm bởi nhiều lớp khác

class x_impl; //forward declare 
class x { 
    public: 
     //methods... 
    private: 
     x_impl* impl_; 
}; 

x .cpp - việc triển khai

#include <conrete_x> 
typedef concrete_x x_impl; //obviously this doesn't work 
//implementation of methods... 

Vì vậy, về cơ bản, tôi muốn người dùng bao gồm tệp x.hpp, nhưng không biết tiêu đề conrete_x.hpp.

Vì tôi có thể sử dụng concrete_x chỉ bởi một con trỏ và nó chỉ xuất hiện như một thành viên dữ liệu cá nhân, nên một khai báo chuyển tiếp là đủ để trình biên dịch biết bao nhiêu không gian để chuẩn bị cho nó. Nó trông khá giống như "thành ngữ pimpl" nổi tiếng.

Bạn có thể giúp tôi với điều này không?

PS. Tôi không muốn sử dụng số void* và bỏ nó xung quanh ..

+0

tại sao bạn không thể có concrete_x kế thừa từ x_impl? – StoryTeller

+0

Chính xác thì vấn đề của bạn là gì? – Grizzly

+0

Tại sao bạn không muốn tự định nghĩa 'lớp x_impl' trong ? –

Trả lời

9

Trên thực tế, nó thậm chí có thể hoàn toàn ẩn từ người sử dụng:

// Foo.hpp 
class Foo { 
public: 

    //... 

private: 
    struct Impl; 
    Impl* _impl; 
}; 

// Foo.cpp 
struct Foo::Impl { 
    // stuff 
}; 

Tôi chỉ muốn nhắc nhở bạn rằng:

  • bạn sẽ cần phải viết một thích hợp destructor
  • và do đó bạn cũng sẽ cần một trình tạo bản sao thích hợp, sao chép toán tử gán, di chuyển hàm tạo và di chuyển toán tử gán

Có nhiều cách để tự động hóa PIMPL, với chi phí của một số ma thuật đen (tương tự như những gì std::shared_ptr làm).

+0

Xin hãy xem ý tưởng của '@Bart van Ingen Schenau'. Giải pháp của bạn tốt hơn là gì? – emesx

+1

@elmes: Tên 'Impl' hoàn toàn bị ẩn, trong khi giải pháp của Bart giới thiệu một tên' x_impl' trong không gian tên bao quanh. Nó thậm chí còn ẩn hơn với cấu trúc lồng nhau 'private'. –

+0

Điều này có hiệu quả nếu cấu trúc bên trong một không gian tên trong tệp cpp? – ChaoSXDemon

2

Điều này sẽ chỉ hoạt động khi khai báo chuyển tiếp tuyên bố tên thực tế của lớp. Vì vậy, một trong hai thay đổi x.hpp tới:

class concrete_x; 
class x { 
    public: 
     //methods... 
    private: 
     concrete_x* impl_; 
}; 

hoặc sử dụng tên x_impl cho lớp được định nghĩa trong tiêu đề <concrete_x>.

0

Đó là giao diện. Xác định giao diện (lớp ảo thuần túy) trong tệp tiêu đề được chia sẻ của bạn và cung cấp cho người dùng. Thừa kế lớp bê tông của bạn từ giao diện và đặt nó trong tệp tiêu đề không được chia sẻ. Thực hiện lớp bê tông trong tệp cpp (bạn thậm chí có thể định nghĩa lớp bê tông bên trong cpp).

+0

Điều này có vẻ ổn, chỉ rằng tôi sẽ có một lớp học ảo chỉ để ẩn chi tiết của một thành viên riêng; C++ vô lý. – emesx

+0

@elmes, nếu bạn đang nghĩ về khả năng của concrete_x khác hơn là cách tiếp cận tự nhiên nhất ... – StoryTeller

4

Để thay thế cho câu trả lời từ @Angew, nếu tên concrete_x không nên được thực hiện được biết đến với những người dùng của lớp x, bạn có thể làm điều này:

trong x.hpp

class x_impl; 
class x { 
    public: 
    x(); 
    ~x(); 
    //methods... 
    private: 
    x_impl* impl_; 
}; 

trong x.cpp

#include <concrete_x> 
class x_impl : public concrete_x { }; 

x:x() : impl_(new x_impl) {} 
x:~x() { delete impl_; } 
+0

Vâng, đây là giải pháp. Nó không phổ biến trong C++ 11, vì 'concrete_x' có thể là cuối cùng. Một câu hỏi cuối cùng: chi phí hiệu suất/bộ nhớ của việc phát sinh lớp học chỉ để che giấu nó là gì? – emesx

+0

So với việc lưu trữ trực tiếp con trỏ đến concrete_x, không có chi phí hiệu năng/bộ nhớ. Chỉ cần một chi phí bảo trì nhỏ (nhỏ) trong đó người bảo trì cần hiểu nó. –

+0

Bạn có thể giải thích những gì đang diễn ra với 'concrete_x'? Tiêu đề mẫu bị thiếu, và nó không rõ ràng với tôi tại sao bao gồm có trong 'x.cpp' so với việc thêm định nghĩa lớp' concrete_x' vào đầu 'x.cpp'. – jww

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