【问题标题】:Restricting field mutation/access to a namespace限制字段​​突变/对命名空间的访问
【发布时间】:2015-04-26 20:17:16
【问题描述】:

我想限制类字段 s.t. 的访问(或突变)。它只能通过专用函数/方法从给定的命名空间访问(或设置)。

由于友谊不是传递或继承的,我的想法是使用一个内部命名空间(应该是实现细节的那个)并将相应的功能作为朋友。例如,如果我们想存储一个秘密,并且只允许内部命名空间内的代码访问它并允许其他人设置它,我希望这样可以解决问题:

#include <cassert>

namespace detail {

// forward declaration doesn't help
// class SecretHolder;
// int GetSecret(SecretHolder& sh);

class SecretHolder {
  int secret_;
  friend int GetSecret(SecretHolder& sh);
 public:
  void SetSecret(int val) { secret_ = val; }
};

// nor does inlining as "friend void GetSecret(SecretHolder& sh) { ... }"
int GetSecret(SecretHolder& sh) { return sh.secret_; }
void DoNothing() { }

} // namespace detail

class User : public detail::SecretHolder {
  // ...
};

int main(int argc, char* argv[]) {
  User u;
  u.SetSecret(42);
  assert(GetSecret(u) == 42); // XXX: why it works without detail:: ???
  // DoNothing(); // this would indeed give a compile error
  return 0;
}

然而,似乎detail::GetSecret 逃脱了封闭的命名空间 detail 在它被定义的地方,所以上面的程序编译并且断言通过了。我很困惑,尤其是在我阅读了 7.3.1.2/3(ISO/IEC 14882:2011)之后:

在命名空间中首先声明的每个名称都是该命名空间的成员 命名空间。如果非本地类中的友元声明首先声明 类或函数友元类或函数是 最内层的封闭命名空间。找不到朋友的名字 不合格查找 (3.4.1) 或合格查找 (3.4.3) 直到 在该命名空间范围内提供了匹配的声明( 在授予友谊的类定义之前或之后)。如果有朋友 函数被调用,它的名字可以通过名字查找找到 考虑来自与 函数参数的类型(3.4.2)。如果在朋友的名字 声明既不是限定的,也不是模板 ID 和声明 是一个函数或一个详细的类型说明符,查找来确定 该实体是否先前已申报,不应考虑任何 最里面的封闭命名空间之外的范围。

我检查了三个主要编译器(GCC、CLANG 和 ICC)的最新版本,其行为似乎是一致的。我在这里缺少什么以及如何实现我最初的意图?

【问题讨论】:

  • 你想找到一种方法来禁用 argument-dependent lookup,我想。

标签: c++ oop namespaces encapsulation friend


【解决方案1】:

因此,我的答案中没有可参考的 ISO 副本,但我将尝试了解正在发生的事情。您的问题不存在于朋友规则中,而是在命名空间中定义的函数将命名空间中声明的类型的对象作为参数时的可见性。

为了说明我在说什么,让我创建另一个函数,看看我们是否可以在不指定命名空间的情况下调用它:

bool SeeYou(SecretHolder * sh)
{
    return sh != nullptr;

}

这些更改允许我们进行更多的编译时检查:

int _tmain(int argc, _TCHAR * argv[])
{
    User u;
    u.SetSecret(42);

// The following works because GetSecret is being given an object that
// exists within the detail namespace.
    assert(GetSecret(u) == 42);

// Let's try some things with see you.
    assert(SeeYou(&u));

// This fails because nullptr is not of the detail namespace.
    //assert(SeeYou(nullptr));  // Compile fails.
    assert(detail::SeeYou(nullptr);  // Compile succeeds.

// Let's see what an object of type SecretHolder does.
    detail::SecretHolder * secretSecretsAreNoFun = new SecretHolder();
    assert(CaughtYou(secretSecretsAreNoFun)); // This works.
    secretSecretsAreNoFun = nullptr;
    assert(CaughtYou(secretSecretsAreNoFun)); // Throws an assert.        

    //assert(CaughtYou(nullptr)); // Compilation fails.

    return 0;
}

编辑:

我忘了提,我没有一个很好的答案你的问题的第二部分,如何实现一个私有密钥,除了我会避免继承包含我的密钥的类。变量secret_ 将始终位于User 对象堆栈上的特定偏移处。

我建议您以命名空间内的每个人都可以查找的方式映射对象和秘密。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-23
    相关资源
    最近更新 更多