【问题标题】:Pimpl, private class forward declaration, scope resolution operatorPimpl,私有类前向声明​​,范围解析运算符
【发布时间】:2016-05-17 18:08:38
【问题描述】:

考虑使用 Pimpl 习语的这两个类:

ClassA:Pimpl 类前向声明​​和变量声明在不同的行中

ClassA.h:

#include <memory>

class ClassA {
public:
    ClassA();
    ~ClassA();
    void SetValue( int value );
    int GetValue() const;

private:

    class ClassA_Impl;
    // ^^^^^^^^^^^^^^ class forward declaration on its own line

    std::unique_ptr<ClassA_Impl> m_pImpl;
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variable declaration on its own line

    //EDIT:
    //Even if we use a raw pointer instead of a smart pointer,
    //i.e. instead of declaring the smart pointer above, if we declare:
    ClassA_Impl *m_pImpl;
    //the situation in the *.cpp files and my questions (below) are the same.


};

ClassA.cpp:

#include "ClassA.h"

class ClassA::ClassA_Impl {
public:
    void SetValue( int value );
    int GetValue() const;
private:
    int value_;   
};

// Private class implementation
void
ClassA::ClassA_Impl::SetValue( int value ) {
    value_ = value;
}

int
ClassA::ClassA_Impl::GetValue() const {
    return value_;
}

// ClassA implementation
ClassA::ClassA() : m_pImpl( new ClassA_Impl() ) {}

ClassA::~ClassA() {}

void
ClassA::SetValue( int value ) {
    m_pImpl->SetValue( value );
}

int
ClassA::GetValue() const {
    return m_pImpl->GetValue();
}

ClassB:Pimpl 类前向声明​​和变量声明在一行

ClassB.h:

#include <memory>

class ClassB {
public:
    ClassB();
    ~ClassB();
    void SetValue( int value );
    int GetValue() const;

    private:
        std::unique_ptr<class ClassB_Impl> m_pImpl;
        //             ^^^^^^^^^^^^^^^^^^ class forward declaration
        //             combined with variable declaration on one line,
        //             in one shot.

        //EDIT:
        //Even if we use a raw pointer instead of a smart pointer,
        //i.e. instead of declaring the smart pointer above, if we declare:
        class ClassB_Impl *m_pImpl;
        //the situation in the *.cpp files and my questions (below) are the same.
};

ClassB.cpp:

#include "ClassB.h"

class ClassB_Impl {
public:
    void SetValue( int value );
    int GetValue() const;
private:
    int value_;
};

// Private class implementation
void
ClassB_Impl::SetValue( int value ) {
    value_ = value;
}

int
ClassB_Impl::GetValue() const {
    return value_;
}

// ClassB implementation
ClassB::ClassB() : m_pImpl( new ClassB_Impl() ) {}

ClassB::~ClassB() {}

void
ClassB::SetValue( int nValue ) {
    m_pImpl->SetValue( nValue );
}

int
ClassB::GetValue() const {
    return m_pImpl->GetValue();
}

问题:

  1. 为什么在 ClassB.h 中将前向声明和变量声明组合在一行中,需要 ClassB_Impl 在 ClassB.cpp 中的私有类的实现中“无作用域”?

    即在 ClassA.cpp 中,私有类方法定义以

    开头
    void ClassA::ClassA_Impl::foo() {...
    

    但在 ClassB.cpp 中,私有类方法定义以

    开头
    void ClassB_Impl::foo() {...
    
  2. 每种方法的含义是什么?哪个更好?

  3. 回应 Galik 的回答的后续问题

    当你在一个语句中组合一个类的前向声明和该类的一个变量的声明......

    //one-liner
    class ClassB_Impl *m_pImpl;
    

    ...这叫什么?这种组合语句有名称吗?为什么ClassB_Impl 没有因为这样的声明而成为ClassB 的内部类?

    比较一下...

    //two-liner
    class ClassA_Impl;
    ClassA_Impl *m_pImpl;
    

    ...在这种情况下ClassA_Impl 确实成为ClassA的内部类。

    为什么单行将ClassB_Impl 放入全局命名空间,而二行将ClassA_Impl 放入ClassA 的命名空间?为什么它们不同?

【问题讨论】:

    标签: c++ forward-declaration pimpl-idiom scope-resolution


    【解决方案1】:

    为什么要结合前向声明和变量声明 ClassB.h 中的一行要求 ClassB_Impl 在 ClassB.cpp中私有类的实现?

    因为在第一个示例中,您将ClassA_Impl 声明为ClassA内部类

    当您在 模板参数 中声明 ClassB_Impl 时,它 ClassB 的一部分。

    每种方法的含义是什么?哪个更好?

    这是一个见仁见智的问题。就我个人而言,我认为内部类是杂乱无章的,而且很难用很少的回报来工作。

    我的首选方法使用单独的 interface 类,这有助于减少您必须重新声明接口的次数。

    见:Is it possible to write an agile Pimpl in c++?

    【讨论】:

    • "当您在不属于 ClassB 的模板参数中声明它时。" - 我编辑了问题以反映即使我使用原始指针而不是智能指针(这样私有类没有在模板参数中声明),情况也是一样的。因此,即使使用原始指针,这是否意味着像 class ClassB_Impl *m_pImpl 这样的声明使 ClassB_Impl not 成为类的一部分?这是否意味着ClassB_Impl 在全局命名空间中可用?
    • 再想一想,我意识到ClassB_Impl 在全局命名空间中;必须如此,这就是为什么在 ClassB.cpp 中我们可以(事实上,我们必须)说void ClassB_Impl::foo() {... 而不是void ClassB::ClassB_Impl::foo() {...。如果情况确实如此,那么我认为我的问题 2 的答案是 ClassA 更好,因为内部 Pimpl 类保持隐藏;与 ClassB 不同,ClassA_Impl 在全局命名空间中不可用,这是我们更喜欢的。想法?我错过了什么吗?
    • @Quokka 我想你现在已经明白了。该类成为ClassB内部类 的唯一方法是将其声明为ClassB 中的类。当您只是声明ClassB 的成员变量之一的类型时,不会发生这种情况。
    猜你喜欢
    • 2017-08-30
    • 2010-09-09
    • 1970-01-01
    • 2012-04-20
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-13
    相关资源
    最近更新 更多