今天在友元類與命名空間結合使用時,遇到一個小問題,總結一下。
這樣一個情境:
A是一個串連類,B是一個用戶端類,在不同的命名空間下,在不同的標頭檔定義,
A希望B能夠訪問到自己的私人成員,所以設定B為其友元類,
B類在執行個體化時,需要用到A類對象進行初始化(建構函式用到A類對象指標)。
涉及到了標頭檔相互包含的問題,標頭檔中對所需的類類型進行前向聲明(forward declaration),然後在.cpp檔案中包含該類類型的標頭檔,
詳細說明可參考http://www.cnblogs.com/sunrack/articles/590384.html
對不同命名空間的類型進行前向聲明時,以下列方式聲明,使用該類型時,需指定其所在命名空間
namespace space_name {class type_name;}
這裡遇到了一個問題,
如果一個類(類型名是A)在特定命名空間(名字是nsa)下,而其友元類(類型名是B)在全域空間下,進行友元類聲明時,目標類使用全域空間網域作業符,
否則,A類會認為B類在nsa下,實際上B類在global空間下,所以導致編譯的時候,提示B類沒有許可權使用A類的私人的資料或函數成員。
Linux下這個規則需要嚴格遵守,在Windows下使用dev-c++發現即使B類不指定全域空間,也能正常編譯和運行,可能是編譯器實現上不同吧。。
總結一下,涉及到不同命名空間的友元類聲明,一定要指定其所在命名空間。
demo代碼如下
a.h
#ifndef A_H#define A_H//forward declarationnamespace nsb {class B;}namespace nsa {class A {public: A(): num_(0) {} int get_num() const { return num_; } friend class nsb::B; //friend class ::B; //if B is in global namespace, need to use "::B"private: int num_;};} // namespace#endif
b.h
#ifndef B_H#define B_H//forward declarationnamespace nsa {class A;}namespace nsb {class B {public: B(nsa::A *a); void set_num_of_A(int n); int get_num_of_A() const;private: nsa::A *pa_;};} // namespace#endif
b.cpp
#include "b.h"#include "a.h"namespace nsb {B::B(nsa::A *pa): pa_(pa) {}void B::set_num_of_A(int n) { pa_->num_ = n;}int B::get_num_of_A() const { return pa_->num_;}} // namespace
test.cpp
#include "a.h"#include "b.h"#include <iostream>using std::cout;using std::endl;int main() { nsa::A a; nsb::B b(&a); cout << a.get_num() << endl; b.set_num_of_A(100); cout << b.get_num_of_A() << endl; return 0;}