2013-04-17 30 views
22

Giả sử tôi có ba lớp C++ FooA, FooB và FooC.Cho phép một lớp "bạn" chỉ truy cập một số thành viên riêng tư

FooA có chức năng thành viên có tên Hello, tôi muốn gọi hàm này trong lớp FooB, nhưng tôi không muốn lớp FooC có thể gọi nó. Cách tốt nhất tôi có thể tìm ra để nhận ra điều này là tuyên bố FooB là một lớp người bạn của FooA. Nhưng miễn là tôi làm điều này, tất cả Thành viên riêng tư và được bảo vệ của FooA sẽ bị lộ diện, điều này hoàn toàn không thể chấp nhận đối với tôi.

Vì vậy, tôi muốn biết nếu có bất kỳ cơ chế nào trong C++ (03 hoặc 11) tốt hơn lớp học friend có thể giải quyết tình huống khó xử này.

Và tôi cho rằng nó sẽ được tốt đẹp nếu cú ​​pháp sau có thể:

class FooA 
{ 
private friend class FooB: 
    void Hello(); 
    void Hello2(); 
private: 
    void Hello3(); 
    int m_iData; 
}; 

class FooB 
{ 
    void fun() 
    { 
     FooA objA; 
     objA.Hello() // right 
     objA.Hello2() // right 
     objA.Hello3() // compile error 
     ojbA.m_iData = 0; // compile error 
    } 
}; 

class FooC 
{ 
    void fun() 
    { 
     FooA objA; 
     objA.Hello() // compile error 
     objA.Hello2() // compile error 
     objA.Hello3() // compile error 
     ojbA.m_iData = 0; // compile error 
    } 
}; 
+1

Không, không có điều đó, theo như tôi biết. –

+0

@summereno: Vì vậy, có thể là một người bạn và họ có nhiều khả năng làm điều đó. –

Trả lời

21

Không có gì để làm cho một lớp là bạn của một chức năng cụ thể là, nhưng bạn có thể làm cho FooB một người bạn của một "chìa khóa" lớp với hàm tạo riêng, và sau đó có FooA::Hello lấy lớp đó làm tham số bị bỏ qua. FooC sẽ không thể cung cấp các thông số và do đó không thể gọi Hello:

Is this key-oriented access-protection pattern a known idiom?

+1

[Mẫu phím tắt] (http://stackoverflow.com/q/3324898/500104), yay ~ – Xeo

+1

Cảm ơn! Tôi đã tìm kiếm nó nhưng không thể nhớ tên: D –

+0

"Không có gì để làm cho một lớp bạn của một chức năng cụ thể" âm thanh sai cách xung quanh, btw. 'friend void foo();' hoạt động hoàn toàn tốt, nhưng tôi nghĩ bạn có nghĩa là không có cách nào để cấp quyền truy cập trực tiếp vào một trong các hàm của bạn. – Xeo

14

Tôi nghĩ rằng bạn có thể sử dụng Attorney-Client đây.

Trong trường hợp ví dụ của bạn nên được như thế này

class FooA 
{ 
private: 
    void Hello(); 
    void Hello2(); 
    void Hello3(); 
    int m_iData; 

    friend class Client; 
}; 

class Client 
{ 
private: 
    static void Hello(FooA& obj) 
    { 
     obj.Hello(); 
    } 
    static void Hello2(FooA& obj) 
    { 
     obj.Hello2(); 
    } 
    friend class FooB; 
}; 

class FooB 
{ 
    void fun() 
    { 
     FooA objA; 
     Client::Hello(objA); // right 
     Client::Hello2(objA); // right 
     //objA.Hello3() // compile error 
     //ojbA.m_iData = 0; // compile error 
    } 
}; 

class FooC 
{ 
    void fun() 
    { 
     /*FooA objA; 
     objA.Hello() // compile error 
     objA.Hello2() // compile error 
     objA.Hello3() // compile error 
     ojbA.m_iData = 0; // compile error*/ 
    } 
}; 
+0

Ví dụ này thật tuyệt vời! – Damian

3

Không, và điều này là không thực sự là một hạn chế. Theo tôi, giới hạn là friend - một vũ khí cùn để hacking xung quanh lỗi thiết kế - tồn tại ngay từ đầu.

lớp học của bạn FooA không có người kinh doanh biết về FooBFooC và "cái nào sẽ có thể sử dụng nó". Cần có giao diện công khai và không quan tâm ai có thể sử dụng giao diện đó. Đó là điểm của giao diện! Các chức năng gọi điện trong giao diện đó phải luôn để lại trạng thái đẹp, an toàn, hài lòng, nhất quán.

Và nếu bạn lo ngại rằng bạn có thể vô tình sử dụng giao diện FooA từ một nơi nào đó bạn không có ý định, tốt, chỉ đơn giản là không làm điều đó; C++ không phải là ngôn ngữ phù hợp để bảo vệ chống lại các loại lỗi người dùng này. Phạm vi kiểm tra của bạn phải đủ trong trường hợp này.

Nói đúng ra, tôi chắc chắn bạn có thể có được chức năng mà bạn đang theo sau với một số mẫu thiết kế phức tạp "khủng khiếp" nhưng, thành thật mà nói, tôi sẽ không bận tâm.

Nếu đây là vấn đề đối với ngữ nghĩa của thiết kế chương trình của bạn, thì tôi lịch sự đề nghị rằng thiết kế của bạn có lỗi.

+4

Điều này sẽ đúng nếu "một lớp" là đơn vị đóng gói tự nhiên duy nhất trong C++. Vì không, câu trả lời này chứa một số điểm không chính xác. Ít nhất, các hàm 'friend' * không phải là vũ khí cùn, chúng không tồn tại để hack xung quanh các lỗi thiết kế, và các lớp học có một doanh nghiệp biết về chúng. Vì vậy, * nhiều nhất * những lời chỉ trích của bạn nên được giới hạn trong các lớp bạn bè, mặc dù trên thực tế cũng có những trường hợp đơn vị đóng gói tự nhiên (không hack, không thiếu sót) là một nhóm các lớp thân thiện. –

+1

"Và nếu lo ngại của bạn là bạn có thể vô tình sử dụng giao diện FooA từ một nơi nào đó bạn không có ý định, tốt, chỉ đơn giản là không làm điều đó;" - gần đây đã sử dụng Python, tôi có rất nhiều thông cảm cho việc này. Rất nhiều thời gian và năng lượng dành lo lắng về kiểm soát truy cập có thể được lưu bằng cách làm cho mọi thứ công khai và sau đó RTFM không gọi hàm chưa được xuất bản. Nhưng đó là một điều phong cách, và những người muốn sử dụng kiểm soát truy cập ở tất cả tìm thấy họ sẽ phải sử dụng nó tốt để có được bất kỳ lợi ích từ nó. –

+1

@SteveJessop trong khi tôi đồng ý rằng cả các hàm 'friend' cũng như' friend' đều là * luôn luôn * một vũ khí cùn, chúng được sử dụng như vậy trong hầu hết các trường hợp. Và chòm sao của lớp OP và yêu cầu "người bạn" bị hạn chế "rõ ràng có mùi. –

0

Toàn bộ ý tưởng của friend là để đưa lớp học của bạn cho bạn bè.

Có 2 cách bạn có thể cụ thể hơn về những gì bạn vạch trần:

  1. Kế thừa từ FooA, rằng cách duy nhất bảo vệ và phương pháp nào được tiếp xúc.

  2. Chỉ làm bạn với một phương pháp nhất định, như vậy chỉ có phương pháp đó sẽ có quyền truy cập:

.

friend void FooB::fun(); 
1

Các giải pháp an toàn nhất là sử dụng một lớp khác là "đi-giữa" cho hai lớp học của bạn, chứ không phải là làm cho một trong số họ một friend. Một cách để làm điều này được diễn tả bằng những câu trả lời bằng @ForEveR, nhưng bạn cũng có thể thực hiện một số tìm kiếm về các lớp proxy và các mẫu thiết kế khác có thể áp dụng.

+0

Tôi tìm hiểu về các lớp proxy hình thức [ở đây] (http://stackoverflow.com/questions/994488/what-is-proxy-class-in-c), và nó thực sự hữu ích, cảm ơn bạn! – summereno

0

Bạn sẽ cần kế thừa. Hãy thử điều này:

// _ClassA.h 
class _ClassA 
{ 
    friend class ClassA; 
private: 
    //all your private methods here, accessible only from ClassA and _ClassA. 
} 

// ClassA.h 
class ClassA: _ClassA 
{ 
    friend class ClassB; 
private: 
    //all_your_methods 
} 

Bằng cách này bạn có: ClassB là người duy nhất để có thể sử dụng ClassA. ClassB không thể truy cập _ClassA phương pháp, riêng tư.

1

Bạn có thể trưng ra một phần giao diện của lớp cho một máy khách được chỉ định bằng cách kế thừa nó từ một lớp giao diện.

class FooA_for_FooB 
{ 
public: 
    virtual void Hello() = 0; 
    virtual void Hello2() = 0; 
}; 

class FooA : public FooA_for_FooB 
{ 
private: /* make them private */ 
    void Hello() override; 
    void Hello2() override; 
private: 
    void Hello3(); 
    int m_iData; 
}; 

class FooB 
{ 
    void fun() 
    { 
     FooA objA; 
     FooA_for_FooB &r = objA; 
     r.Hello() // right 
     r.Hello2() // right 
     objA.Hello3() // compile error 
     objA.m_iData = 0; // compile error 
    } 
}; 

class FooC 
{ 
    void fun() 
    { 
     FooA objA; 
     objA.Hello() // compile error 
     objA.Hello2() // compile error 
     objA.Hello3() // compile error 
     objA.m_iData = 0; // compile error 
    } 
}; 

Kiểm soát truy cập này được tăng cường bởi lớp cơ sở FooA_for_FooB. Bằng cách tham chiếu loại FooA_for_FooB, FooB có thể truy cập các thành viên được xác định trong phạm vi FooA_for_FooB. Tuy nhiên, FooC không thể truy cập các thành viên đó vì chúng đã được ghi đè là thành viên riêng tư trong FooA. Mục đích của bạn có thể đạt được bằng cách không sử dụng loại FooA_for_FooB trong phạm vi FooC hoặc bất kỳ địa điểm nào khác ngoại trừ FooB, có thể được lưu giữ mà không chú ý nhiều.

Cách tiếp cận này không cần phải friend, giúp mọi việc trở nên đơn giản.

Điều tương tự có thể được thực hiện bằng cách đặt mọi thứ riêng tư trong lớp cơ sở và chọn lọc và hiển thị một số thành viên là công khai trong lớp dẫn xuất. Cách tiếp cận này đôi khi có thể yêu cầu downcast xấu xí, mặc dù. (Vì lớp cơ sở sẽ trở thành "tiền tệ" trong toàn bộ chương trình.)

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