我们的class A
有一个内class A::Impl
。 class AllowedToAccess
应该能够从A::Impl&
获得class A
,其他任何类都不应该这样做,甚至不知道class A::Impl
存在。
class A_1
{
public:
class Impl;
Impl& impl();
const Impl& impl() const;
private:
std::unique_ptr<Impl> m_impl;
};
class A_2
{
private:
friend class AllowedToAccess;
class Impl;
std::unique_ptr<Impl> m_impl;
};
class A_3
{
private:
friend class AllowedToAccess;
class Impl;
class ImplContainer
{
friend class A_3;
std::unique_ptr<Impl> impl;
} m_implContainer;
Impl& impl(); // return *m_implContainer.impl;
const Impl& impl() const; // return *m_implContainer.impl;
};
以下是一些代码来说明我的想法。
A_1
优点:m_impl
是安全的。
缺点:不应该知道A::Impl
的课程会知道它(尽管他们不知道A::Impl
到底是什么意思。)
A_2
优点:不应该了解A::Impl
的课程也不会知道。
缺点:m_impl
不受保护(AllowedToAccess
可能只是将其设置为nullptr
)。
A_3
优点:不应该了解A::Impl
的课程不会知道,m_impl
是安全的。
缺点:ImplContainer
的Boilerplate代码。
有更好的想法吗?
编辑:提出这样的解决方案。
template <typename T>
class Accessor
{
private:
friend class AllowedToAccess;
typedef typename T::Impl impl_t;
static typename T::Impl& impl(T& t)
{
return *t.m_impl;
}
static const typename T::Impl& impl(const T& t)
{
return *t.m_impl;
}
};
class A
{
private:
friend class Accessor<A>;
class Impl;
std::unique_ptr<Impl> m_impl;
};
class A::Impl
{
public:
void f() const
{
}
};
class AllowedToAccess
{
public:
AllowedToAccess()
{
const A a;
const Accessor<A>::impl_t& impl = Accessor<A>::impl(a);
impl.f();
}
};
有一个代码实现层,所有这些类都将作为朋友添加到Accessor。
为了进一步隐藏内容,访问者只能在公共代码中向前声明为template <typename T> class Accessor;
。并在私人代码中定义。
答案 0 :(得分:2)
如果可以将所有使用m_impl本身的逻辑移动到基类,则可以使m_impl对派生类不可访问,但允许它引用Impl
,然后使{{{ 1}}类派生类的朋友:
Access
答案 1 :(得分:1)
这个怎么样:
class A_4
{
public:
class Impl;
Impl& impl();
const Impl& impl() const;
private:
std::unique_ptr<Impl> m_impl;
};
class A_4::Impl
{
private:
friend class A_4;
friend class AllowedToAccess;
Impl();
~Impl();
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
// All members private!
};
如果代码可以调用impl()
或编写A_4::Impl
,如果没有任何用处可以实际使用它,那么这并不重要。
答案 2 :(得分:1)
我不知道它是否是一个更好的主意,但你可以提供自己的AllowedToAccess
实现,这将安全地暴露所需的接口。我称之为Accessor
。
class Base{
double priv = 0;
public:
friend class Accessor;
};
class Accessor {
public:
int& priv(Base& b) { return b.priv; };
};
现在,类可以通过Accessor
访问公开的私有。语义稍有改变,因为你像accessor.priv(b) = something
一样使用它,但是你可以完全控制暴露的接口。我认为此变体中的Accessor
可能具有所有静态方法,因为无论如何都会传递被访问的对象。所以语义是Accessor::priv(b)
。
我想这是 A_3 示例的变体,代码已移至朋友类。但它不会污染A_
类。
另一种选择是以简单包装的形式提供您自己的Accessor
类。
class Accessor {
public:
Accessor(Base b) _b(b);
int priv() { return _b.priv; };
int priv(int val) { _b.priv = val; };
int& priv_r() { return _b.priv; };
private:
Base& _b;
};
此处的界面也可完全自定义,您可以访问以下功能:
Base b;
Accessor interface(b);
interface.priv(42);
你也可以绝对安全地禁止从Accessor
派生(有一些技巧),所以没有人可以通过Accessor
的邪恶实现来派生和搞砸你的对象。我认为这会有点偏执。