gpt4 book ai didi

c++ - 干净的 C++ 颗粒 friend 等价物? (答案: Attorney-Client Idiom)

转载 作者:IT老高 更新时间:2023-10-28 13:59:01 26 4
gpt4 key购买 nike

为什么 C++ 有任何人都可以调用的 public 成员和将 all private 成员公开的 friend 声明给定外来的类或方法,但没有提供将特定成员公开给给定调用者的语法?

我想用一些例程来表达接口(interface),这些例程只能由已知的调用者调用,而不必让这些调用者完全访问所有私有(private)内容,这感觉像是一件合理的事情。到目前为止,我能想到的最好的自己(下)和其他人的建议都围绕着各种间接性的习语/模式,我真的只是想要一种方法来拥有single,简单的类定义明确表明哪些调用者(比我的 child 绝对任何人更细化)可以访问哪些成员。表达以下概念的最佳方式是什么?

// Can I grant Y::usesX(...) selective X::restricted(...) access more cleanly?
void Y::usesX(int n, X *x, int m) {
X::AttorneyY::restricted(*x, n);
}

struct X {
class AttorneyY; // Proxies restricted state to part or all of Y.
private:
void restricted(int); // Something preferably selectively available.
friend class AttorneyY; // Give trusted member class private access.
int personal_; // Truly private state ...
};

// Single abstract permission. Can add more friends or forwards.
class X::AttorneyY {
friend void Y::usesX(int, X *, int);
inline static void restricted(X &x, int n) { x.restricted(n); }
};

我离成为软件组织专家还差得很远,但感觉界面的简单性和最小权限原则在语言的这方面直接不一致。对于我的愿望,一个更清晰的例子可能是一个 Person 类,其中声明了 takePill(Medicine *) tellTheTruth()forfeitDollars( unsigned int) 只有 PhysicianJudgeTaxMan 实例/成员方法,甚至应该考虑调用。每个主要接口(interface)方面都需要一次性代理或接口(interface)类让我很不舒服,但如果你知道我遗漏了什么,请说出来。

接受来自 Drew Hall 的答案: Dr Dobbs - Friendship and the Attorney-Client Idiom

上面的代码最初将包装类称为“代理”而不是“律师”,并使用指针而不是引用,但在其他方面与 Drew 发现的相同,然后我认为这是最广为人知的解决方案。 (不要太用力地拍自己的背……)我还更改了“restricted”的签名来演示参数转发。这个习惯用法的总成本是每个权限集一个类和一个 friend 声明,每个批准的调用者一个 friend 声明,每个权限集每个公开的方法一个转发包装器。下面的大部分更好的讨论都围绕着转发调用样板展开,一个非常相似的“关键”习语以较少的直接保护为代价避免了这种样板。

最佳答案

有一个非常简单的模式,它被追溯称为 PassKey , 即 very easy in C++11 :

template <typename T>
class Key { friend T; Key() {} Key(Key const&) {} };

然后:

class Foo;

class Bar { public: void special(int a, Key<Foo>); };

还有调用站点,在任何 Foo 中方法,看起来像:

Bar().special(1, {});

注意:如果你卡在 C++03 中,请跳到文章末尾。

代码看似简单,它嵌入了一些值得细说的关键点。

模式的关键在于:

  • 调用 Bar::special需要复制 Key<Foo>在调用者的上下文中
  • 仅限 Foo可以构造或复制 Key<Foo>

值得注意的是:

  • 派生自 Foo 的类无法构造或复制 Key<Foo>因为友元不是传递性的
  • Foo本身无法传下Key<Foo>任何人都可以调用Bar::special因为调用它不仅需要持有一个实例,还需要制作一个拷贝

因为 C++ 是 C++,所以需要避免一些问题:

  • 复制构造函数必须是用户定义的,否则为public默认情况下
  • 默认构造函数必须是用户定义的,否则为public默认情况下
  • 默认构造函数必须手动定义,因为= default将允许聚合初始化绕过手动用户定义的默认构造函数(从而允许任何类型获取实例)

这很微妙,我建议您复制/粘贴上述 Key 的定义。逐字记录,而不是试图从内存中重现。


允许委托(delegate)的变体:

class Bar { public: void special(int a, Key<Foo> const&); };

在这个变体中,任何拥有 Key<Foo> 实例的人可以调用Bar::special , 所以即使只有 Foo可以创建Key<Foo> ,然后它可以将凭据传播给受信任的副手。

在这个变体中,为了避免流氓副官泄露 key ,可以完全删除复制构造函数,这允许将 key 生命周期绑定(bind)到特定的词法范围。


在 C++03 中呢?

嗯,这个想法是相似的,除了 friend T;不是一个东西,所以必须为每个持有者创建一个新的 key 类型:

class KeyFoo { friend class Foo; KeyFoo () {} KeyFoo (KeyFoo const&) {} };

class Bar { public: void special(int a, KeyFoo); };

该模式足够重复,可能值得一个宏来避免拼写错误。

聚合初始化不是问题,但= default语法也不可用。


特别感谢多年来帮助改进此答案的人们:

  • Luc Touraille ,因为在 class KeyFoo: boost::noncopyable { friend class Foo; KeyFoo() {} }; 的评论中指向我完全禁用复制构造函数,因此仅适用于委托(delegate)变体(防止存储实例)。
  • K-ballo , 指出 C++11 如何通过 friend T; 改善这种情况

关于c++ - 干净的 C++ 颗粒 friend 等价物? (答案: Attorney-Client Idiom),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3217390/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com