干净的 C++ 粒度朋友等效吗? (答案:律师-委托人习语)

2023-12-05

为什么C++有public任何人都可以呼叫的成员以及friend暴露的声明all private会员到given外部类或方法,但不提供向给定调用者公开特定成员的语法?

我想用一些例程来表达接口,这些例程只能由已知的调用者调用,而不必授予这些调用者对所有私有变量的完全访问权限,这感觉像是一个合理的事情。到目前为止,我自己能想到的最好的办法(如下)和其他人的建议都围绕着不同间接性的习语/模式,我真的只是想要一种方法single,简单的类定义,明确指示调用者(比me, 我的孩子们, or 绝对任何人) 可以访问哪些成员。表达以下概念的最佳方式是什么?

// 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() and forfeitDollars(unsigned int)只有Physician, Judge, or TaxMan实例/成员方法甚至应该分别考虑调用。每个主要接口方面都需要一次性代理或接口类,这让我感到不舒服,但如果您知道我遗漏了一些东西,请大声说出来。

答案接受自德鲁·霍尔: 多布斯博士 - 友谊和律师委托人习语

上面的代码最初将包装类称为“Proxy”而不是“Attorney”,并使用指针而不是引用,但在其他方面与 Drew 发现的相同,我认为这是最好的众所周知的解决方案。 (不要太用力地夸奖自己......)我还更改了“restricted”的签名来演示参数转发。此习惯用法的总体成本是每个权限集一个类和一个朋友声明,每个经过批准的调用者集一个朋友声明,以及每个权限集每个公开方法一个转发包装器。下面的大部分更好的讨论都围绕着转发调用样板,一个非常相似的“Key”习惯用法以较少的直接保护为代价避免了该样板。


有一个非常简单的模式,后来被称为PassKey,这是在 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 困境,请跳至本文末尾。

该代码看似简单,但它嵌入了一些值得详细阐述的关键点。

该模式的关键在于:

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

值得注意的是:

  • 派生类Foo无法构造或复制Key<Foo>因为友谊是不可传递的
  • Foo本身无法传承Key<Foo>任何人都可以打电话Bar::special因为调用它不仅需要保留一个实例,还需要制作一个副本

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

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

这非常微妙,我建议您复制/粘贴上面的定义Key逐字逐句地而不是试图从记忆中复制它。


允许授权的变体:

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

在此变体中,任何拥有以下实例的人Key<Foo>可以打电话Bar::special,所以即使只有Foo可以创建一个Key<Foo>,然后它可以将凭证传播给值得信赖的副官。

在此变体中,为了避免流氓中尉泄漏密钥,可以完全删除复制构造函数,这允许将密钥生命周期绑定到特定的词法范围。


那么在 C++03 中呢?

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

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

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

该模式重复性足够高,因此可能值得使用宏来避免拼写错误。

聚合初始化不是问题,但话又说回来了= default语法也不可用。


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

  • 吕克·图雷耶,在评论中指出我class KeyFoo: boost::noncopyable { friend class Foo; KeyFoo() {} };完全禁用复制构造函数,因此仅适用于委托变体(防止存储实例)。
  • K-ballo,指出 C++11 如何改善这种情况friend T;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

干净的 C++ 粒度朋友等效吗? (答案:律师-委托人习语) 的相关文章

  • 如何在 ASP.NET MVC 中将 XML 文件发送到客户端

    在 ASP NET MVC 中 我有一个数据库表 我想在某个视图页面上有一个按钮 如果某个用户单击该按钮 我的应用程序将生成包含数据库中所有行的 XML 文件 然后 应将包含 XML 的文件发送到客户端 以便用户看到下载弹出窗口 同样 我希
  • Boost MPI 在监听列表时不会释放资源?

    这是一个后续问题如何释放 boost mpi request https stackoverflow com questions 44078901 how do i free a boostmpirequest 我在监听列表而不是单个项目时
  • 头文件中实现的函数的静态与内联

    我想到的方式inline在 C 中用于链接 作用域 我把它放在同一个篮子里extern and static对于全局对象 通常 对于在头文件中实现的函数 我的首选解决方案是将其设为静态 In Foo h static void foo Do
  • 如何从不同的线程访问控件?

    如何从创建控件的线程以外的线程访问控件 避免跨线程错误 这是我的示例代码 private void Form1 Load object sender EventArgs e Thread t new Thread foo t Start p
  • 无法将参数从 `const char *` 转换为 `char *`

    鉴于此代码 void group build int size std string ips Build the LL after receiving the member list from bootstrap head new memb
  • C# 中的抽象类和接口类有什么不同?

    C 中的抽象类和接口类有什么不同 An 接口不是类 它只是一个contract定义了public一个类的成员must实施 抽象类只是一个类 您从中可以cannot创建一个实例 通常您会使用它来定义一个基类 该基类定义了一些virtual方法
  • 为什么 BinaryFormatter 可以序列化 Action<> 但 Json.net 不能

    尝试序列化 反序列化 Action 尝试我的 1天真 JsonConvert SerializeObject myAction JsonConvert Deserialize
  • asp.net core http 如果没有内容类型标头,则删除 `FromBody` 忽略

    我在 http 中使用 bodyDELETE要求 我知道目前删除主体是非标准的 但是允许的 使用时出现问题HttpClient它不允许删除请求的正文 我知道我可以使用SendAsync 但我宁愿让我的 API 更加灵活 我希望这个机构是可选
  • 列表到优先队列

    我有一个 C 大学编程项目 分为两个部分 在开始第二部分时应该使用priority queues hash tables and BST s 我 至少 在优先级队列方面遇到了麻烦 因为它迫使我自己重做第一部分中已经实现的许多代码 该项目是关
  • 查找方法不适用于 EF6.1 模拟

    我已经使用这些 msdn 指南设置了模拟 使用模拟框架进行测试 EF6 及以上 http msdn microsoft com en us data dn314429 var bsAc db BusAcnts FirstOrDefault
  • linq where 子句和 count 导致 null 异常

    除非 p School SchoolName 结果为 null 否则下面的代码将起作用 在这种情况下 它会导致 NullReferenceException if ExistingUsers Where p gt p StudentID i
  • 从 C# 调用时无法识别 Powershell 命令

    这是这个的延续Question https stackoverflow com questions 66280000 powershell object returns null 66280138 noredirect 1 comment1
  • 在 C# 命令行应用程序中包含并执行 EXE

    所以我找到了一个很棒的小 EXE 命令行应用程序 我们将其称为 program exe 它输出一些我想用 C 操作的数据 我想知道是否有一种方法可以将program exe 打包 到我的Visual Studio项目文件中 这样我就可以将编
  • 如何释放字符串未使用的容量

    我正在程序中处理很多字符串 这些字符串数据在读入我的程序后的整个生命周期内都不会改变 但由于 C 字符串保留了容量 因此浪费了大量肯定不会被使用的空间 我尝试释放这些空间 但没有成功 以下是我尝试过的简单代码 string temp 123
  • 基础设施 - 同步和异步接口和实现? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 在实现库 基础设施时 并且该 API 的用户希望同步和异步使用代码 我读到混合同步和异步并不是一个好主意 例如 同步实现包括等待异步实现 显然
  • 如何在控制台程序中获取鼠标位置?

    如何在 Windows 控制台程序中用 C 获取鼠标单击位置 点击时返回鼠标位置的变量 我想用简单的文本命令绘制一个菜单 这样当有人点击时 游戏就会注册它并知道位置 我知道如何做我需要做的一切 除了单击时获取鼠标位置 您需要使用 Conso
  • 为什么 C++ 标准没有将 sizeof(bool) 定义为 1?

    Size of char signed char and unsigned char由 C 标准本身定义为 1 个字节 我想知道为什么它没有定义sizeof bool also C 03 标准 5 3 3 1 说 sizeof char s
  • 强制函数调用的顺序?

    假设我有一个抽象基类 并且我想要一个必须由派生类实现的纯虚方法 但我想确保派生方法以特定顺序调用函数 我可以做什么来强制执行它 I E base class virtual void doABC 0 virtual void A 0 vir
  • Asp.Net Core 中的 SSL 不起作用

    我从 Visual Studio 创建了一个简单的 Web 应用程序Web Application Net Core 具有个人用户帐户授权的模板 然后 我启用了 SSLProject gt MyProject Properties 将带有
  • 如何通过API退出Win32应用程序?

    我有一个使用 Win32 API 编写的 C Win32 应用程序 我希望强制它在其中一个函数中退出 有没有类似的东西Exit or Destroy or Abort 类似的东西会终止它吗 哎呀呀呀呀呀呀 不要做任何这些事情 exit 和

随机推荐

  • Java if 与 try/catch 开销

    Java 中使用 a 是否有任何开销试着抓块 而不是if block 假设所附代码不要求如此 例如 采用以下两个字符串 安全修剪 方法的简单实现 public String tryTrim String raw try return raw
  • 使用“IN”SQL 函数中的值将逗号分隔值从 .NET 传递到存储过程

    我有一个类似于以下的 SQL 查询 create procedure test param1 nvarchar max as begin select from table where column1 in param1 end 现在我需要
  • 如何使用相同的对象属性创建多个关系?

    我正在使用 protege 5 来开发本体 我创造了has composition作为对象属性 在我的本体中 相同的 object property 用于不同的域及其各自的范围 例如has compositionA 是 B 并且has co
  • 是否可以使用Python的默认互联网浏览器打开某些网址?

    我希望 python 使用计算机默认的网络浏览器打开某个地址 这可能吗 绝对可以 使用网页浏览器 module
  • 当调用 Activity 被销毁时,Android 新 Activity 上的 getIntent 为 NULL

    你们有解决以下问题的方法吗 在注册活动的 onDestroy 中 当用户按下后退按钮时 我调用一个新活动 以便用户可以输入当天的一些最终生产数据 然后通过电子邮件发送报告 问题是 在刚刚启动的活动中 对 getIntent 的调用返回 nu
  • 删除表中过滤的行而不是整个行

    过滤表格时 如何仅删除表格行而不删除整个工作表行 Dim TradeTable As Excel ListObject Set TradeTable Sheets Pre Trade ListObjects PreTradeTable On
  • 如何在 git 中强制执行作者/提交者身份验证?

    目前我们的项目中使用git进行版本控制 开发环境是内网 带有AD的Windows服务器 无法访问Internet 有没有办法配置 git 以使用作者和提交者的开发者域登录名 我们希望阻止开发人员自己提供这些信息 我在谷歌搜索这个问题时发现了
  • iPhone中非系统字体的名称

    我想向我的 iPhone iPad 应用程序添加非系统字体 我已将字体文件添加到项目中 并将字体添加到UIAppFontsinfo plist 的属性 加载我正在使用的字体fontWithName of UIFont 但是name字体的名称
  • 使用 nvd3 库显示单系列多条形图

    Does anyone know how I would go making a multi bar graph to be single series In a working example that i ve seen of how
  • C:为什么我们要包含声明但不定义的头文件?

    在较高的层面上 我知道我们使用 include使其他文件中的代码可用于当前文件的语句 但我不明白为什么我们要包含一个头文件 其中包含声明但没有定义 也许我需要更多地了解编译 链接过程才能完全理解其机制 但是是否有一个我一开始就未能掌握的高级
  • ASP.Net Web API OData - 消费者可以自由地查询他们想要的任何内容?

    我刚刚阅读了有关 OData 查询的 ASP Net Web API 支持的内容 并且在协调查询过滤的外部暴露方面遇到了麻烦 这本质上为集成商提供了向数据库抛出任意查询过滤器的能力 而不考虑最佳查询计划 不应查询的字段等等 如何清理 ODa
  • 截断小数不四舍五入[重复]

    这个问题在这里已经有答案了 可能的重复 c 如何将小数值四舍五入到小数点后两位 用于页面上的输出 我想像下面这样截断小数 i e 2 22939393 gt 2 229 2 22977777 gt 2 229 您可以使用数学轮 decima
  • 角度 $interval 在超过“count”参数后是否会自行取消?

    关于 Angular 中的 interval 服务的快速问题 查看文档 间隔 它们警告您手动取消间隔 但您可以选择在初始化时提供计数参数 一旦计时器 滴答作响 超过了分配的计数 它会自行取消还是只是停止调用该函数并在后台继续运行 TL 博士
  • 带有深思熟虑的冠层的 scipy

    我正在评估 Enthought 包 我安装了32位canopy 从下载https www enthought com downloads 在Ubuntu中 sudo bash canopy 1 0 1 rh5 32 sh 经过测试我没有看到
  • Symfony 在预持久上将数据添加到对象

    我有一个用于创建文档的表格 一方面 我可以添加名称和描述 在旁边我可以选择一个或多个机构创建的文档属于谁 每个机构被分配一个specific市场 一共有7个市场 所以一个市场可以有多个代理机构 但一个代理机构只能属于一个市场 我想要实现的是
  • 如何使用c++运行批处理文件?

    如何使用c 运行批处理文件 我对此一无所知 请参阅 system 函数 http www cplusplus com reference clibrary cstdlib system
  • 将函数保留在头文件中还是源文件中更好

    是将函数保留在头文件中并将它们包含到主源文件中更好 还是将函数保留在源文件中并将它们链接到主源文件中更好 一些库将函数保存在单独的源文件中 这些源文件在特殊的头文件中声明 当您将该头文件包含在主源文件中时 您就链接了所有这些函数 但是 它为
  • ActionBarSherlock 不支持浅色主题警报对话框?

    正如标题所说 我正在使用 actionBarSherlock 库和浅色主题 有时我需要使用alertDialog Builder 类显示一个对话框 问题是 无论我尝试什么 主题都不适用于对话框本身 该主题应该适用于新 API 和旧 API
  • 如何在 Ubuntu 14.04 x64 上安装 Theano 并配置它以使用 GPU?

    我尝试按照以下说明进行操作在当前 Ubuntu 上轻松安装优化的 Theano但它不起作用 每当我使用 GPU 运行 Theano 脚本时 它都会给我错误消息 CUDA已安装 但设备gpu不可用 错误 无法获取可用gpu的数量 未检测到支持
  • 干净的 C++ 粒度朋友等效吗? (答案:律师-委托人习语)

    为什么C 有public任何人都可以呼叫的成员以及friend暴露的声明all private会员到given外部类或方法 但不提供向给定调用者公开特定成员的语法 我想用一些例程来表达接口 这些例程只能由已知的调用者调用 而不必授予这些调用