【问题标题】:Private namespace in source files源文件中的私有命名空间
【发布时间】:2013-05-29 12:08:05
【问题描述】:

我对私有方法和函数有疑问。 假设我有一些不需要在类中的实用方法。但是这些相同的方法需要调用我不想向用户公开的其他方法。例如:

Suspect.h

namespace Suspect {
  /**
  *  \brief This should do this and that and more funny things.
  */
  void VerbalKint(void);  // This is for you to use
}

Suspect.cpp

namespace Suspect {
  namespace Surprise {
    /**
    * \brief The user doesn't need to be aware of this, as long 
    *        the public available VerbalKint does what it should do.
    */
    void KeyserSoze(void) {
      // Whatever
    }
  } // end Surprise

  void VerbalKint(void) {
    Surprise::KeyserSoze();
  }
}

所以,这个布局有效。包含Suspect.h 时,只有VerbalKint 可见。 这也可以使用一个类并将VerbalKint 标记为静态来实现:

class Suspect {
public:
  // Whatever
  static void VerbalKint(void);
private:
  static void KeyserSoze(void);
};

我想知道这两种方法之间是否有任何区别。一个比另一个更好(更快、更容易维护)吗?

你有什么想法?

【问题讨论】:

  • 如果您希望示例相同,您也应该在课堂上创建KeyserSoze static。但是,如果你把它作为一个类放在头文件中,那么包括那个头文件的每个人都可以看到有一个名为KeyserSoze的函数,所以你不能“隐藏”它。
  • 一方面:你不能在不包含类定义的情况下创建静态函数的前向声明。
  • @JoachimPileborg,谢谢。我错过了!
  • 另外,如果您在源文件中使用 anonymous 命名空间,您实际上是不允许导出其中的函数。就像现在一样,如果有人用正确的命名空间前缀制作了KeyserSoze 的原型,那么他们可以使用它,如果它是匿名的,他们就不能。
  • Suspect 如果应该是对象类型,则应该是类,如果不应该是对象类型,则应该是命名空间。

标签: c++ namespaces


【解决方案1】:

如果函数是“免费的”,您应该在*.cpp 中使用匿名命名空间:

namespace Suspect {
namespace Surprise {
namespace {
    void KeyserSoze(void) {
      // Whatever
    }
} // end anon
} // end Surprise
} // end Suspect

甚至:

namespace {
    void KeyserSoze(void) {
      // Whatever
    }
} // end anon

这使它远离客户端,因此他们在链接时无法访问、依赖或与您的导出冲突。如果定义可见,它还可以保留不必要的声明,从而减少它们的编译时间和潜在的链接时间或二进制大小。最后,它使它成为私有的,因此他们不能依赖它,并且您不需要维护它以供他们使用。如果您选择,您仍然可以将这些传递给外部世界(KeyserSoze() 的情况下的函数指针)。

在其他时候,最好在你的类中声明一个私有成员函数,然后在*.cpp 中定义它(如果可能的话)。通常,当您需要与班级建立更密切的关系时(例如,当您需要访问某些成员时),您会选择这种方法。您说问题中不是这种情况,但我只是重申何时应该使用私人成员。

【讨论】:

  • 这是最完整的答案,涵盖了我想到的一些主题,所以接受了!
【解决方案2】:

实际上,即使您没有在任何标头中声明该函数,眼睛也不可见;如果用户编写声明,它仍然可供用户使用。

在 C++ 中,隐藏在文件级别声明的符号的机制是:

  • static 用于(全局)变量和函数
  • namespace { ... }(匿名命名空间),随心所欲(更通用、更详细)

例如:

// Suspect.cpp

namespace Suspect {

    static void KeyserSore() {}

    void VerbalKing() { KeyserSore(); }

}

【讨论】:

    【解决方案3】:

    最好的方法是在 Suspect.cpp 的未命名命名空间中定义所有辅助函数,而不是在 Suspect::Surprise 命名空间中。

    在你的情况下,这将是:

    namespace{
    void KeyserSoze(){ ... };
    }
    

    您可以在 Suspect.cpp 中简单地调用 KeyserSoze 而无需任何命名空间说明符。

    您可以在此处找到更多信息:Unnamed/anonymous namespaces vs. static functions

    另一种选择是将KeyserSoze 声明为static,但标准不建议这样做。 C++ 标准在第 7.3.1.1 节未命名命名空间,第 2 段中写道:

    在命名空间范围内声明对象时不推荐使用 static 关键字,未命名命名空间提供了更好的选择

    【讨论】:

    • static 在 C++11 中已被弃用。
    【解决方案4】:

    将某些东西放入类或命名空间的主要区别在于,您不能在另一个头文件中的类中添加额外的静态函数。

    这个: 呵呵

    namespace Fred {
       void Somefunc();
    }
    

    b.h

    namespace Fred {
       void Anotherfunc();
    }
    

    工作,尽管 a 和 b 都不知道彼此对他们的命名空间做了什么。这可能会导致问题,例如:

    c.h

    namespace Fred {
       void Thirdfunc();
    }
    

    d.h

    namespace Fred {
       bool Thirdfunc();
    }
    

    在你开始运行程序之前,一切都很好,很花哨......

    这虽然不是不可能的,但对于课程来说可能性要小得多。

    在您的示例中,只有一个源文件,您可能还想考虑使用匿名命名空间,因为它将声明限制在文件范围内,因此您文件之外的人无法意外访问它们(或与它们发生冲突)。

    【讨论】:

    • 感谢您说明在不同文件中修改命名空间时可能出现的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多