源文件中的私有命名空间

Private namespace in source files

提问人:Adri C.S. 提问时间:5/29/2013 最后编辑:Adri C.S. 更新时间:5/29/2013 访问量:15329

问:

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

嫌疑人.h

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

嫌疑人.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.hVerbalKintVerbalKint

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

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

你有什么想法?

C++ 命名空间

评论

1赞 Some programmer dude 5/29/2013
如果您希望示例等效,您也应该在课堂上制作。但是,如果您将其作为类放在头文件中,那么包括该头文件的每个人都可以看到有一个名为 的函数,因此您无法“隐藏”它。KeyserSozestaticKeyserSoze
2赞 selalerer 5/29/2013
首先:如果不包含类定义,就无法创建静态函数的前向声明。
0赞 Adri C.S. 5/29/2013
@JoachimPileborg,谢谢。我错过了!
2赞 Some programmer dude 5/29/2013
此外,如果在源文件中使用匿名命名空间,则实际上禁止导出其中的函数。就像现在一样,如果有人使用正确的命名空间前缀制作了原型,那么他们可以使用它,如果它是匿名的,他们就不能使用它。KeyserSoze
1赞 aschepler 5/29/2013
Suspect如果它应该是对象类型,则应为类,如果不应是对象类型,则应为命名空间。

答:

10赞 Jeff Dun 5/29/2013 #1

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

在您的情况下,这将是:

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

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

您可以在此处找到有关此内容的更多信息:未命名/匿名命名空间与静态函数

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

在命名空间范围内声明对象时,不推荐使用 static 关键字,unnamed-namespace 提供了更好的替代方法

评论

2赞 avakar 5/29/2013
static在 C++11 中未弃用。
6赞 Matthieu M. 5/29/2013 #2

实际上,即使当您不在任何标头中声明该函数时,该函数是肉眼不可见的;如果用户编写声明,它仍然可供用户使用。

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

  • static对于(全局)变量和函数
  • namespace { ... }(匿名命名空间)用于您想要的任何内容(更通用,更冗长)

例如:

// Suspect.cpp

namespace Suspect {

    static void KeyserSore() {}

    void VerbalKing() { KeyserSore(); }

}
2赞 Tom Tanner 5/29/2013 #3

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

这: 啊

namespace Fred {
   void Somefunc();
}

c.I.(英语:c.I)

namespace Fred {
   void Anotherfunc();
}

工作,尽管 A 和 B 都不知道对方对他们的命名空间做了什么。可以想象,这可能会导致问题,例如:

C.H.(英语:C.H)

namespace Fred {
   void Thirdfunc();
}

D.H.(英语:D.H)

namespace Fred {
   bool Thirdfunc();
}

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

虽然这并非不可能,但上课的可能性要小得多。

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

评论

0赞 Adri C.S. 5/29/2013
感谢您说明在不同文件中修改命名空间时可能出现的问题。
25赞 justin 5/29/2013 #4

如果函数是“free”的,则应在以下位置使用匿名命名空间:*.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

评论

1赞 Adri C.S. 5/29/2013
这是最完整的答案,涵盖了我想到的一些主题,所以接受了!
0赞 Robert M. 3/30/2023
古老的线,我知道......是否也应该将函数原型放在匿名命名空间内的头文件中?
0赞 theRPGmaster 8/17/2023
@RobertM。是的。您应该将声明放在匿名命名空间块中,就像定义一样。