【问题标题】:How to access private member of impl class from original class using PIMPL approach如何使用 PIMPL 方法从原始类访问 impl 类的私有成员
【发布时间】:2020-09-15 14:24:40
【问题描述】:

我正在使用 PIMPL 方法,并希望从常规类访问实现类的私有成员。有什么合法的方法可以做到这一点吗?在下面的代码中,我想访问两个私有成员。其中一个是属性,另一个是方法。

这是我的代码:

戴夫.h

#include "DaveImpl.h"

class Dave {
    public:
        Dave ();
        int getAge ();
    private:
        int getIq ();
        DaveImpl* _impl;
};

戴夫.cc

 #include "Dave.h"

Dave::Dave () {
    _impl = new DaveImpl ();
}

int Dave::getAge () {
    return _impl->age;
}

int Dave::getIq () {
    return _impl->getIq ();
}

DaveImpl.h

class DaveImpl {
    public:
        DaveImpl ();
    private:
       int age;
       int iq;
       int getIq ();
};

DaveImpl.cc

#include "DaveImpl.h"

DaveImpl::DaveImpl () {
    age=60;
    iq=75;
}

int DaveImpl::getIq () {
    return iq;
}

当我编译上述代码时,我收到以下消息:

Dave.cc:8:19: error: ‘int DaveImpl::age’ is private within this context
INFO: 1>     8 |     return _impl->age;
Dave.cc:12:26: error: ‘int DaveImpl::getIq()’ is private within this context
INFO: 1>    12 |     return _impl->getIq ();

我有什么方法可以访问上述场景中的“age”属性或“getIq”方法吗?

请注意,我坚持认为实现类中的成员和方法是私有的。很遗憾,我无法改变这一点。

【问题讨论】:

  • 为子类设置protected,为其他类设置public。如果是private,则无法在课堂外访问。
  • 不幸的是,我坚持认为实现类中的成员是私有的。
  • 你可以做一个公共的getter,除此之外,就是这样。
  • @didjek The implementation class is somebody else's code and it is an API I am not permitted to break 那么您需要使用 API 提供的功能来访问这些成员。如果它没有为此提供 API,那么您不应该直接使用这些成员。
  • @didjek,如果你可以随意访问私有成员,private 修饰符的意义何在?

标签: c++ private-members pimpl-idiom private-methods pimpl


【解决方案1】:

PIMPL 是一种隐藏的实现,特别是隐藏实现细节以不破坏公共接口。如果您声明您不能更改 DaveImpl,因为它会破坏 API,而不仅仅是实现细节,因此根本不应该是 PIMPL,因此您应该考虑可能的设计缺陷。

此外,如果有一个名为 DaveImpl 的类具有 Dave 应该公开的类的实现细节,为什么 DaveImpl 应该对 Dave 隐藏任何内容,因为它是专门为 Dave 而不是为其他任何人编写的?因此,说到重点,根本没有理由成为私人会员。您可以将DaveImpl 更改为struct,将getters/setters 更改为所有内容或创建friend class。最合乎逻辑的是第一个。

实际的 PIMPL 应该如何实现

戴夫.h

class Dave {
    public:
        Dave ();
        int getAge () const;
    private:
        // no reason at all to have this method. It's private so it's 
        // an implementation, then it could be directly called through
        // the pimpl.
        // int getIq () const;

        struct DaveImpl;
        std::unique_ptr<DaveImpl> _impl;
};

戴夫.cc

 #include "Dave.h"

struct Dave::DaveImpl {
    DaveImpl() { ... }

    int age;
    int iq;
 
    //int getIq (); no reason at all to have a getter
};

Dave::Dave () 
 : _impl(std::make_unique<DaveImpl>()){
}

int Dave::getAge () const {
    return _impl->age;
}

在您的实现中,无论您需要在何处调用iq,都可以直接使用_impl-&gt;iq

【讨论】:

    【解决方案2】:

    您可以在公共范围内的 impl 类中声明 getter:

    class DaveImpl {
    public:
        DaveImpl ();
        int getAge() const {return age;} 
        int getIq() const {return iq;}
    private:
       int age;
       int iq;
    };
    

    并使用它们:

    int Dave::getAge () {
        return _impl->getAge();
    }
    
    int Dave::getIq () {
        return _impl->getIq ();
    }
    

    您还可以将 Dave 类声明为您的 impl 类的朋友,并且 Dave 类将有权访问 impl 的私有字段:

    class DaveImpl {
        friend class Dave;
        public:
            DaveImpl ();
        private:
           int age;
           int iq;
           int getIq ();
    };
    

    【讨论】:

    • friend class DaveImpl; 允许DaveImpl 访问Dave 的成员,而不是相反。
    • 除此之外,OP 声称DaveImpl 是外来代码,不能对其进行任何更改。
    • 不完全是,我可以向 DaveImpl 添加代码,但我不能更改现有代码(例如,将成员从私有更改为公共,或更改现有方法的签名等)。
    • @didjek 如果您声称无法更改外国代码,您怎么能做到这一点?
    • @didjek 但是为什么将成员公开将是一个重大变化而添加公共成员函数则不会呢?这没有多大意义。
    猜你喜欢
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 2013-03-28
    相关资源
    最近更新 更多