2010-06-01 20 views
12

Đôi khi, khái niệm về sự riêng tư của C++ chỉ baffles tôi :-)ủy thành các phần riêng

class Foo 
{ 
    struct Bar; 
    Bar* p; 

public: 

    Bar* operator->() const 
    { 
     return p; 
    } 
}; 

struct Foo::Bar 
{ 
    void baz() 
    { 
     std::cout << "inside baz\n"; 
    } 
}; 

int main() 
{ 
    Foo::Bar b; // error: 'struct Foo::Bar' is private within this context 

    Foo f; 
    f->baz();  // fine 
} 

Kể từ Foo::Barprivate, tôi không thể tuyên bố b trong main. Tuy nhiên, tôi có thể gọi các phương thức từ Foo::Bar tốt. Tại sao cái này lại được phép? Đó có phải là một tai nạn hay do thiết kế?


Oh chờ đợi, nó được tốt hơn:

Foo f; 
auto x = f.operator->(); // :-) 
x->baz(); 

Mặc dù tôi không được phép để đặt tên cho loại Foo::Bar, nó hoạt động tốt với auto ...


Noah đã viết:

loại các tên được định nghĩa trong một định nghĩa lớp không thể được sử dụng bên ngoài lớp của chúng mà không có trình độ chuyên môn.

Chỉ cần cho vui, đây là cách bạn có thể có được ở các loại từ bên ngoài:

#include <type_traits> 

const Foo some_foo(); 

typedef typename std::remove_pointer<decltype(some_foo().operator->())>::type Foo_Bar; 
+8

Để viết tắt ví dụ mã của bạn, về cơ bản bạn hỏi liệu pháp lý có thiết kế để trả về kiểu riêng ('Foo :: Bar *') từ hàm thành viên công cộng ('Foo :: operator ->()' hay không)? – stakx

+0

@stakx Hmmm, tôi đoán vậy :) +1 – fredoverflow

+0

Về ví dụ mã 'auto' của bạn, tôi nghĩ đó là viết tắt của' auto int'. Điều này thực sự biên dịch và chạy một cách chính xác? – stakx

Trả lời

6

Cố gắng tìm bất cứ điều gì trong tiêu chuẩn đó sẽ đánh vần nó ra một cách chi tiết nhưng tôi không thể. Điều duy nhất tôi có thể tìm thấy là 9.9:

Tên loại tuân thủ chính xác các quy tắc phạm vi giống như các tên khác. Đặc biệt, các tên được định nghĩa trong một định nghĩa lớp không thể được sử dụng bên ngoài lớp của chúng mà không có trình độ chuyên môn.

Về cơ bản, tên của Foo :: Thanh là riêng tư đối với Foo, không phải định nghĩa. Vì vậy, bạn có thể sử dụng Thanh bên ngoài của Foo, bạn chỉ có thể không tham chiếu đến chúng theo loại kể từ khi tên đó là riêng tư.

Quy tắc tra cứu tên cho các thành viên cũng dường như có một số ảnh hưởng đến điều này. Tôi không thấy bất cứ điều gì cụ thể tham khảo "lớp lồng nhau" và do đó họ sẽ không được phép (nếu tôi không tìm thấy bất cứ điều gì trong thực tế là bởi vì nó không có).

3

Tôi không thể cung cấp câu trả lời đầy đủ, nhưng có thể là điểm bắt đầu. Các đặc điểm kỹ thuật C++ 1998 bao gồm các ví dụ mã sau dưới đoạn 11,3 [class.access] (p 175).:

class A 
{ 
    class B { }; 
public: 
    typedef B BB; 
}; 

void f() 
{ 
    A::BB x; // OK, typedef name A::BB is public 
    A::B y; // access error, A::B is private 
} 

Trong ví dụ này, một loại tin được "xuất bản" thông qua một công typedef. Mặc dù nó không giống với việc xuất bản một loại thông qua chữ ký hàm thành viên, nó giống nhau.

+2

Cùng văn bản trong khoản 11, đoạn 4 của C++ 0x FCD; Tôi nghĩ rằng đoạn 5 giải thích lý do tại sao bạn có thể làm điều đó: "Cần lưu ý rằng đó là quyền truy cập vào các thành viên và các lớp cơ sở được kiểm soát, chứ không phải khả năng hiển thị của họ.Name thành viên vẫn hiển thị và chuyển đổi ngầm đến lớp cơ sở vẫn được xem xét khi các thành viên đó không thể tiếp cận được. Việc giải thích một cấu trúc nhất định được thiết lập mà không liên quan đến kiểm soát truy cập. Nếu việc giải thích được thiết lập là sử dụng tên thành viên hoặc lớp cơ sở không thể tiếp cận, thì cấu trúc bị hình thành. " –

+0

* @ Niall C.:* Tôi không chắc chắn rằng sự khác biệt giữa khả năng truy cập và khả năng hiển thị cung cấp câu trả lời. Khả năng hiển thị không có vẻ là vấn đề, ở đây. Và ví dụ mã trên vẫn làm cho một kiểu riêng có thể truy cập được (mặc dù thời trang hạn chế). – stakx

+0

Mặc dù 'Foo :: Bar' không thể truy cập được bên ngoài' Foo', các thành viên của nó có thể nhìn thấy và truy cập được thông qua 'Bar *' được trả về bởi toán tử 'operator ->()' .. –

1

Tôi nghĩ điều này là do thiết kế. Bạn không thể tạo một cách rõ ràng cá thể của Foo::Bar nhưng nó có thể được trả về từ các hàm thành viên và sau đó bạn có thể chuyển nó cho các hàm thành viên khác. Điều này cho phép bạn ẩn chi tiết triển khai của lớp học.