【问题标题】:Opposite of friend declaration朋友声明的对面
【发布时间】:2018-10-19 12:52:58
【问题描述】:

假设我们有一个具有私有构造函数的类,通过friend 我们可以允许某些特定的类仍然创建该类的对象:

class Foo
{
 friend class Bar;
private:
  Foo();
};

class Bar
{
  Bar()
  {
    //create a Foo object
  }
};

现在如果我想要 friend 的反面,Foo 看起来像这样:

class Foo
{
 //enemy/foe??? class Bar; (if only)
public:
  Foo();
};

然后Bar 的任何方法都无法访问Foo 构造函数/创建Foo 的对象,但其他类可以(因为它是public)。

class Bar
{
  Bar()
  {
    Foo foo; //compiler error
  }
};

这样的结构是否可行,还是我坚持将Foo 保持私密并为所有课程添加朋友?

【问题讨论】:

  • 很遗憾没有。原因是一个人必须与他们的朋友保持亲密关系,而他们的敌人则更接近。 ;)
  • 你为什么想要那个?听起来你的设计有问题。 friend 应谨慎使用;如果您必须将它用于“所有课程”,那么您做错了。
  • @SebastianRedl 这就是为什么我不想采取friend 的所有方法,但Bar 方法,重新阅读问题。 Bar 不应该能够访问 Foo,这在现实世界的例子中要复杂得多,但我有充分的理由。现在,Foo 的构造函数是公开的,没有使用 friend。
  • “我有充分的理由这样做。” - 我非常好奇。
  • @SebastianRedl Bar 包含微控制器的事务逻辑,Foo 允许通过旧版 API 访问该控制器的某些部分。互换使用可能会导致问题。运行时检查已经到位,所以即使它意外发生,它本身也不会导致问题,但我宁愿添加一个编译时错误来解释为什么不允许这样做。

标签: c++ c++17 friend


【解决方案1】:

这样的东西是不存在的,而且是极其没有意义的。想象一下你有这样的情况:

class Foo
{
  enemy class Bar;

public:
  Foo() {}
};

class Bar
{
  void evil() { Foo{}; }
};

没有什么能阻止Bar 的实现者这样做:

class Bar
{
  void evil() { do_evil(*this); }
};

void do_evil(Bar &self)
{
  Foo{};
}

do_evil 不是Bar 的成员(它是一个全局函数),因此它不是敌人。因此,可以轻松规避这种不友好。

【讨论】:

  • 很公平,#define private public 也是一种方式:P。 const_cast 也是一个肮脏的黑客。对我来说,这并不是要 100% 确保无论你多么努力都无法创建 Foo 对象,而是要在方法中发出编译器错误,以便客户知道他们可能做错了什么Foo 在 Bar 内,因此不应试图规避它。
  • @SombreroChicken 问题是#define provate public导致代码无效;这个简单的解决方法没有。
  • @SombreroChicken - 使用宏重新定义语言关键字会产生未定义的行为。
  • @SombreroChicken 如果你可以修改Bar,你可以在其中声明一个本地的Foo 一些无意义的东西或者会产生你想要的错误的东西,这样使用名称Foo 将使用嵌套的,而不是全局的。
【解决方案2】:

这确实做不到,但也许以下对你来说就足够了:

template <typename T> struct Tag {};

class Foo
{
public:
    template <typename T>
    Foo(Tag<T>) {}

    Foo(Tag<Bar>) = delete;

    // ...
};

因此要求“创造者”“识别”自己。

class Bar
{
    Bar()
    {
        Foo foo{Tag<Bar>{}}; //compiler error

        // Foo foo{Tag<void>{}}; // valid as we can cheat about identity.
    }
};

【讨论】:

    【解决方案3】:

    C++ 中没有这样的概念。

    公共属性将始终是公共的,但您可以限制Foo 的暴露,例如,通过使构造函数受保护并且仅对选定的类可见(尽管建议限制friend)。也可以将Foo 设为Bar2 的受保护类,因为只有Bar2 或其子级会实际使用它。

    【讨论】:

      【解决方案4】:

      正如其他人已经说过的,你的欲望打破了封装的想法,因为你不能总是知道你的敌人是谁。

      但是,仍然有可能得到(几乎)你想要的东西:

      #include <type_traits>
      
      struct enemy;  // We need a forward declaration of your enemy
      
      struct my_class {
      
          // The access is done using a factory, where the caller has to tell
          // us his own name
          template <class T>
          struct make{
              static_assert(!std::is_same<T,enemy>::value,"you are my enemy.");
              friend T;
          private:
              my_class operator() () { return my_class{}; }
          };
      
      
      private:
          my_class(); // This is the constructor we care about
      
      };
      
      struct no_enemy {
          void bar() {
              my_class a = my_class::make<no_enemy>{}();  // works
          }
      };
      
      
      struct enemy {
          void bar() {
              my_class b = my_class::make<enemy>{}();     // error: static_assert failed
              my_class c = my_class::make<no_enemy>{}();  // error: foo is "private"
          }
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-30
        • 1970-01-01
        • 1970-01-01
        • 2011-02-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多