2011-04-21 73 views
5

我正在嘗試使用pimpl模式並在匿名命名空間中定義實現類。這在C++中可能嗎?我的失敗嘗試如下所述。pimpl與匿名命名空間兼容嗎?

有沒有可能解決這個問題,而無需將實現移動到名稱空間(或全局名稱空間)?

class MyCalculatorImplementation; 

class MyCalculator 
{ 
public: 
    MyCalculator(); 
    int CalculateStuff(int); 

private: 
    MyCalculatorImplementation* pimpl; 
}; 

namespace // If i omit the namespace, everything is OK 
{ 
    class MyCalculatorImplementation 
    { 
    public: 
     int Calculate(int input) 
     { 
      // Insert some complicated calculation here 
     } 

    private: 
     int state[100]; 
    }; 
} 

// error C2872: 'MyCalculatorImplementation' : ambiguous symbol 
MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation) 
{ 
} 

int MyCalculator::CalculateStuff(int x) 
{ 
    return pimpl->Calculate(x); 
} 

回答

6

號,類型必須至少聲明就可以使用指針類型之前,並把匿名命名空間中的標題將沒有真正的工作。但是,無論如何,你爲什麼要這麼做呢?如果你真的想要隱藏實現類,使其成爲一個私人內部類,即

// .hpp 
struct Foo { 
    Foo(); 
    // ... 
private: 
    struct FooImpl; 
    boost::scoped_ptr<FooImpl> pimpl; 
}; 

// .cpp 
struct Foo::FooImpl { 
    FooImpl(); 
    // ... 
}; 

Foo::Foo() : pimpl(new FooImpl) { } 
+1

這是我使用時間最長,也是如此,直到有人向我指出,如果出口類'Foo',還出口類'美孚: :FooImpl',這通常不是你想要的... – 2011-04-21 14:56:39

+0

@mmutz是否_export_表示與MS有關的'__declspec(dllexport)'?如果是的話,我可能不需要擔心。 – anatolyg 2011-04-21 15:01:45

+0

@anatolyg:是,或'__attribute __((visibility = default))'在GCC/ELF系統上。 – 2011-04-21 15:04:17

1

不,你不能這樣做。您必須提前申報Pimpl類:

class MyCalculatorImplementation; 

並聲明該類。如果您然後將該定義放入未命名的命名空間中,那麼您將創建另一個類(anonymous namespace)::MyCalculatorImplementation,這與::MyCalculatorImplementation無關。

如果這是任何其他的命名空間NS,你可以修改前瞻性聲明,包括命名空間:

namespace NS { 
    class MyCalculatorImplementation; 
} 

但具名命名空間,是魔術,因爲它是將解決別的東西時標題包含在其他翻譯單元中(只要將該標題包含到其他翻譯單元中,您就會聲明一個新類)。

但是在這裏不需要使用匿名命名空間:類聲明可以是公共的,但是定義(在實現文件中)僅對實現文件中的代碼可見。

1

如果你確實想在你的頭文件中向前聲明的類名和模塊文件的匿名命名空間的實現,然後作出聲明類的接口:

// header 
class MyCalculatorInterface; 

class MyCalculator{ 
    ... 
    MyCalculatorInterface* pimpl; 
}; 



//module 
class MyCalculatorInterface{ 
public: 
    virtual int Calculate(int) = 0; 
}; 

int MyCalculator::CalculateStuff(int x) 
{ 
    return pimpl->Calculate(x); 
} 

namespace { 
    class MyCalculatorImplementation: public MyCalculatorInterface { 
     ... 
    }; 
} 

// Only the ctor needs to know about MyCalculatorImplementation 
// in order to make a new one. 
MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation) 
{ 
} 
+0

您仍然會使用'Interface'類污染公共類的命名空間 - >沒有收穫。 – 2011-04-21 17:44:48

2

是。有一個解決這個問題的方法。將頭文件中的指針聲明爲void *,然後在實現文件中使用reinterpret強制轉換。

注意:這是否是一個理想的解決方法是另一個問題。正如人們經常說的那樣,我將把它作爲讀者的練習。

請參見下面的實現:

class MyCalculator 
{ 
public: 
    MyCalculator(); 
    int CalculateStuff(int); 

private: 
    void* pimpl; 
}; 

namespace // If i omit the namespace, everything is OK 
{ 
    class MyCalculatorImplementation 
    { 
    public: 
     int Calculate(int input) 
     { 
      // Insert some complicated calculation here 
     } 

    private: 
     int state[100]; 
    }; 
} 

MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation) 
{ 
} 

MyCalaculator::~MyCalaculator() 
{ 
    // don't forget to cast back for destruction! 
    delete reinterpret_cast<MyCalculatorImplementation*>(pimpl); 
} 

int MyCalculator::CalculateStuff(int x) 
{ 
    return reinterpret_cast<MyCalculatorImplementation*>(pimpl)->Calculate(x); 
} 
+0

我認爲如果'reinterpret_cast'實際上是必要的,古典pimpl不會那麼受歡迎。這顯然不是要走的路...... – m8mble 2016-07-18 19:26:04

+0

@ m8mble要清楚,提出的問題不是它是否是_advisable_;問題是這是否是可能的。如上所述,儘管存在其他相反的答案,但這絕對是可能的。 – markshiz 2016-07-20 03:04:54

+1

@ m8mbl你是否需要的問題完全是另一個問題。所以這裏的倒票似乎有點多。該帖子仍然是信息豐富的,並提供了一種可能對某人有用的解決方法。 – markshiz 2016-07-20 03:10:46