命名空间级别的访问

4
我可以帮助您翻译,以下是需要翻译的内容:

我有以下情况:

namespace MyFramework {
  class A {
    void some_function_I_want_B_to_use() {}
  };
  class B {
    B() {
      some_function_I_want_B_to_use() {}
    }
  };
}

我希望 some_function_I_want_B_to_use 不在 MyFramework 命名空间外可见,但我希望任何在 MyFramework 内部的人都能看到它(或者仅对类 B 可见也可以)。我有很多这样的方法,隐藏它们不让它们成为 MyFramework 的公共 API 的唯一方法是使 MyFramework 中的所有类都成为友元吗?我还考虑将所有“低级”类放在 B 中,但在确定它可以从 B 内部访问 A 的所有方法但不能从 MyFramework 外部访问之前,我不想走这条路。
重新表述一下,我有一个完全创建于一个命名空间内的框架,每个类都有对使用该框架的公众有用的方法。然而,每个类也有一些复杂化公共 API 但对框架正常运行是必需的方法。

“visible” 是什么意思?如果函数的名称可以在头文件中找到,但该函数是“private”的,那么它是否可见? - anatolyg
不需要(对于我的目的来说)。我想将“公共”API列为仅公共方法。例如,有3个公共方法可以清楚地使用类,但有20多个方法会使类的使用方式变得不太明显。 - Hamy
some_function_I_want_B_to_use需要是A的一个方法吗?还是可以作为MyFramework的命名空间函数? - EHuhtala
我忘记了这是否会授予类访问彼此的私有数据成员,但如果MyFramework是一个类,而每个类都在其下面(适当的成员为私有),那会怎样呢?或者,我认为外观(设计模式)正好适用于这种情况;如果您愿意为每个类创建一个公共外观,则这是一个不错的选择。 - Rollie
@EHuhtala - 我不这么认为...我怀疑MyFramework的内部类(假设你指的是内部类而不是子类)能否访问MyFramework的另一个内部类的私有字段。这只是重新提出了最初的观点,也许使用友元类是解决这个问题的“正确”方法,尽管我的理解是它们不适用于面向对象的代码。 - Hamy
显示剩余3条评论
3个回答

4
我希望 some_function_I_want_B_to_use 不在 MyFramework 命名空间以外可见,但我希望它对于 MyFramework 内部的任何人都是可见的。
总之,您想要类似于Java中的软件包功能。
不幸的是,命名空间不能实现此功能。命名空间中包含的每个类都可以从命名空间外部访问:命名空间是开放的。
通常的解决方案是添加另一个用于实现细节的命名空间:
namespace MyFramework
{
    // Implementation details
    // Should not be used by the user
    namespace detail
    {
        class A
        {
            public:
                void func();
        };
    }

    class B
    {
        public:
            B()
            {
                A a;
                a.func();
            }
    };
}

不要忘记添加注释,说明detail命名空间不应该被用户使用。

2

在Boost等库中,常见的约定是使用名为detail的嵌套命名空间。

如果您想要强制实施可访问性,可以始终改用名为detail的嵌套class。该类提供可访问性检查,但缺乏像命名空间那样的可扩展性。然而,detail作用域很少需要扩展。

因此,尽管有些丑陋,

namespace my_framework {
    class detail
    {
    private:
        static void some_function_I_want_B_to_use() {}

    public:
        class A
        {};

        class B
        {
            B() { some_function_I_want_B_to_use(); }
        };
    };

    typedef detail::A A;        // "using detail::A"
    typedef detail::B B;        // "using detail::B"
}  // namespace my_framework

顺便提一下,注意B类(从问题中得出)有一个私有的默认构造函数,因此无法创建它的任何实例。

2

Pimpl惯用语,通常被称为编译防火墙,这就是你要寻找的东西。整个Qt都是使用这种惯用语实现的。

// A.hpp
namespace MyFramework {
  class A {
  private:
    class Private;

    Private* implementation;
  };
}

// A_Private.hpp
#include "A.hpp"

namespace MyFramework {
  class A::Private {
  public:
    void some_function_I_want_B_to_use() {}
  };
}

// A.cpp
#include "A_Private.hpp"

namespace MyFramework {
A::A() {
  implementation->some_function_I_want_B_to_use();
}
}

// B.hpp
#include "A.hpp"

namespace MyFramework {
  class B {
    B();

    A a;
  };
}

// B.cpp
#include "A_Private.hpp"

namespace MyFramework {
B::B() {
  a.implementation->some_function_I_want_B_to_use();
}
}
注意:当然,A_Private.hpp 不会出现在你框架的最终分发版本中的 include 目录下,即它仍然是包私有的,依照您的需要。
这个例子非常基础。当然,它可以变得更加先进和稳健。此外,Pimpl 还有很多其他优点。有关所有这些信息,请参见以下链接:

1
如果需要一个仅包含头文件的模块,该怎么办? - Cheers and hth. - Alf
@Cheers and hth. - Alf,那么你就没那么幸运了,这也是为什么Boost使用detail命名空间约定的确切原因(轻微的开销是另一个原因)。然而,作者并没有提供这样的要求,因此这个解决方案完全符合他的问题。 - Alexander Shukaev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接