2010-06-17 43 views
15

Xin xem đoạn code ví dụ dưới đây:Chức năng thành viên lớp lồng nhau không thể truy cập chức năng của lớp kèm theo. Tại sao?

class A 
{ 
private: 
    class B 
    { 
    public: 
     foobar(); 
    }; 
public: 
    foo(); 
    bar(); 
}; 

Trong hạng A thực hiện & B:

A::foo() 
{ 
    //do something 
} 

A::bar() 
{ 
    //some code 
    foo(); 
    //more code 
} 

A::B::foobar() 
{ 
    //some code 
    foo(); //<<compiler doesn't like this 
} 

Cờ biên dịch các cuộc gọi đến foo() trong phương pháp foobar(). Trước đó, tôi đã có foo() là hàm thành viên riêng của lớp A nhưng thay đổi thành công khai giả định rằng hàm B không thể nhìn thấy nó. Tất nhiên, nó không giúp được gì. Tôi đang cố gắng tái sử dụng chức năng được cung cấp bởi phương pháp của A. Tại sao trình biên dịch không cho phép chức năng này gọi? Như tôi thấy, chúng là một phần của cùng một lớp bao quanh (A). Tôi nghĩ rằng vấn đề trợ năng cho meebers lớp lồng nhau cho kèm theo lớp trong tiêu chuẩn C++ đã được giải quyết.

Làm thế nào tôi có thể đạt được những gì tôi đang cố gắng làm mà không viết lại cùng một phương thức (foo()) cho B, giữ B lồng trong A?

Tôi đang sử dụng trình biên dịch VC++ ver-9 (Visual Studio 2008). Cảm ơn sự giúp đỡ của bạn.

Trả lời

14

foo() là một chức năng thành viên không tĩnh của A và bạn đang cố gọi nó mà không có một cá thể.
Lớp lồng nhau B là một lớp riêng biệt chỉ có một số đặc quyền truy cập và không có bất kỳ kiến ​​thức đặc biệt nào về các phiên bản hiện tại của A.

Nếu B nhu cầu truy cập vào một A bạn phải cung cấp cho nó một tham chiếu đến nó, ví dụ .:

class A { 
    class B { 
     A& parent_; 
    public: 
     B(A& parent) : parent_(parent) {} 
     void foobar() { parent_.foo(); } 
    }; 
    B b_; 
public: 
    A() : b_(*this) {} 
}; 
+3

+1, chỉ cần một nitpick - 'parent' có lẽ không phải là tên tốt nhất cho biến thành viên ở đây - dễ nhầm lẫn với thừa kế. –

+1

Tôi chỉ muốn đề cập rằng có một lý do thiết kế rất tốt cho lớp B lồng nhau có tham chiếu đến lớp A. – manifest

+0

Cảm ơn bạn đã giải thích bằng ví dụ. Tiêu chuẩn C++ 11.8, mà tôi đoán rằng nó đã thay đổi, nói về việc truy cập thành viên lớp bằng lớp lồng nhau. Tôi biết rằng gcc cho phép truy cập bởi lớp lồng nhau (Tôi chắc chắn về nó) nhưng trình biên dịch MS VC không. Hmm ... thú vị. – Rahul

0

Nếu bạn muốn sử dụng lại chức năng từ A thì bạn nên kế thừa từ A không tổ B bên trong nó.

+1

Tôi không mở rộng fucntionality của A trong B, vì vậy, không có nhu cầu để kế thừa B từ A. Ngoài ra, tôi muốn giữ B ẩn với người dùng lớp A. – Rahul

1

Đây là một automagic, mặc dù lừa có thể nonportable (làm việc trên VC++ từ 6.0 mặc dù). Lớp B phải là thành viên của lớp A để làm việc này.

#ifndef OUTERCLASS 
#define OUTERCLASS(className, memberName) \ 
    reinterpret_cast<className*>(reinterpret_cast<unsigned char*>(this) - offsetof(className, memberName)) 
#endif 

class A 
{ 
private: 
    class B 
    { 
    public: 
     void foobar() { 
      A* pA = OUTERCLASS(A, m_classB); 
      pA->foo(); 
     } 
    } m_classB; 
public: 
    foo(); 
    bar(); 
}; 
+0

Cảm ơn bạn, Igor vì đã trả lời câu hỏi của tôi và đưa ra một ví dụ. Bạn và Georg đã thực hiện gần như tương tự với một tham chiếu trở lại lớp bên ngoài, tuy nhiên, tôi đã chọn phản ứng của Georg vì nó sạch hơn. – Rahul

+0

Không phải lo lắng, đây là sau một hack tìm kiếm khá khó chịu. Có nói rằng, nó đã được khá đáng tin cậy và nó kinda vui vẻ :) –

0

Về cơ bản những gì Georg Fritzsche nói

#include <iostream> 
#include <cstring> 
using namespace std; 

class A 
{ 
private: 
    class B 
    { 
    A& parent_; 
    public: 
     //B(); //uncommenting gives error 
     ~B(); 
     B(A& parent) : parent_(parent) {} 

     void foobar() 
     { 
     parent_.foo(); 
     cout << "A::B::foo()" <<endl; 
     } 

     const std::string& foobarstring(const std::string& test) const 
     { 
     parent_.foostring(test); cout << "A::B::foostring()" <<endl; 
     } 
    }; 
public: 
    void foo(); 
    void bar(); 
    const std::string& foostring(const std::string& test) const; 
    A(); 
    ~A(){}; 
    B b_; 
}; 

//A::B::B() {}; //uncommenting gives error 
A::B::~B(){}; 

A::A():b_(*this) {} 


void A::foo() 
{ 
    cout << "A::foo()" <<endl; 
} 

const std::string& A::foostring(const std::string& test) const 
{ 
    cout << test <<endl; 
    return test; 
} 

void A::bar() 
{ 
    //some code 
    cout << "A::bar()" <<endl; 
    foo(); 
    //more code 
} 

int main(int argc, char* argv[]) 
{ 
A a; 
a.b_.foobar(); 
a.b_.foobarstring("hello"); 

return 0; 
} 

Nếu bạn bỏ ghi chú các nhà xây dựng mặc định B bạn sẽ nhận được một lỗi

+0

Tôi đã không nhận được lỗi. – cbinder

+0

@cbinder cố gắng bỏ ghi chú 'A :: B :: B() {};' quá – enthusiasticgeek

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