何时使用命名空间或结构体?

14

我刚刚在阅读http://www.cplusplus.com/doc/tutorial/namespaces/上的一些内容,似乎结构体也能够做到同样的事情?甚至一个类也可以。也许有人在这里可以更好地定义命名空间是什么,以及它与结构体 / 类的区别?

7个回答

24

命名空间和类类型不能完成相同的任务。命名空间主要用于将类型和函数分组以避免名称冲突,而类类型则包含操作数据的数据和操作。

如果仅使用类类型来分组函数和对象,则必须使它们为静态:

struct X {
    static void f();
};

如果没有使用static,你必须创建类的实例才能使用它们。在此处使用命名空间会更加合适:

namespace X {
    void f();
}

另一个重要的事情是using声明和指令:

namespace X {
    void f();
    void g();
}

void h() {
    using X::f;
    f(); // f() now visible in current scope
    using namespace X;
    f(); g(); // both visible
}

对于类类型来说,没有任何机制可以实现这一点。

与命名空间相比,类类型的优势在于您可以拥有具有不同状态的多个实例 - 如果需要,请使用类类型。


6
此外,结构体和命名空间的设计初衷是解决不同的问题。因此,当您使用命名空间时,您传达的是与使用结构体时不同的关于您试图实现的目标的信息。 - wilhelmtell
1
“using” 构造在许多情况下是不明智的。维护者通常比用户更清楚这一点。如果维护者想要确保在有大量开发人员的环境中显式使用他的库,建议使用 struct/static。我喜欢在这种用法前加上解释:struct X { // create an explicit-only namespace - Erik Aronesty
命名空间的使用最好通过样式规则(通过审查或linting规则)进行更好的控制,而不是重载其他结构。 - Georg Fritzsche

11

看起来每个人都在发表自己的观点,那我也加入进来。

首先要明确一点:namespacestruct是完全不同的东西:它们有不同的语法和不同的语义。

很明显:

  • struct引入了一个类型,你可以将它用作模板参数
  • namespace可以分布在多个文件中

从语法上来说:

  • 两者都可以被“别名”,命名空间使用namespace ns = mylong::name::space;,结构体使用typedef mylong::name::Space lilstruct;
  • ADL(或相关参数查找)专门针对命名空间进行优化

从语义上来说:

  • namespace仅定义符号的范围,这允许将一起工作的对象(类和自由函数)组合在一起,同时将它们与世界其他部分隔离开来(名称冲突)。因此,它通常表示项目中的逻辑单元(对于小型项目,只有一个命名空间)。
  • structclass定义数据和操作方法之间的逻辑绑定,这是封装的基石。它通常有一个明确的责任和一些不变量。

请注意,有时候structclass只是用来绑定一起工作的对象,而没有任何逻辑,例如struct Person { std::string name, firstName; };

话虽如此:在C++中,没有必要使用structstatic方法。这只是Java或C#及其“纯”面向对象方法的扭曲。C++支持自由函数,因此没有使用它们的理由,特别是因为它对封装更好(它们不能访问私有/受保护的部分,因此你无法破坏不变量,并且它们也不依赖于类的表示)。


2
这可能不是最佳答案,但仍然是一个很好的答案。它真正解释了如何和为什么。我喜欢它! - Dogunbound hounds

5
如果您不希望人们在使用您的类时使用C++的“using”功能,这可能是危险的,并且在复杂代码中经常不被建议,那么请使用具有静态结构体。换句话说:如果您的函数应始终使用“group :: function”进行引用,则可以通过声明为struct来限制用户。此外,重要的是,在旧版本的C ++中可以前向声明结构体,但在C ++ 11之前无法使用名称空间进行此操作。请考虑:
std::string out zip::pack(const std::string &in)
std::string out zip::unpack(const std::string &in)

在这种情况下,要求用户指定zip::是有意义的。它简短、具体且信息量丰富。如果没有它,底层函数的名称就会含糊不清。使用带有静态变量的结构体。
考虑:
std::string out CorpDataUtils::zipPack(const std::string &in)
std::string out CorpDataUtils::zipUnpack(const std::string &in)

这些肯定应该放在一个命名空间中。命名空间的名称很长,不太具有信息性,可能更多地与维护它的人员组织有关 - 这很好...但实际上它应该是一个命名空间...而不是一个结构体。


4

如果可以使用命名空间来完成,就使用命名空间。

结构体不仅仅定义了一个作用域,它还定义了一个类型。


2
当命名空间嵌套不一致时,我觉得很烦人。例如,我讨厌Boost库中有些库有自己的嵌套命名空间,而其他库却没有,因为这样不统一。为什么可以说boost::bind(),但必须说boost::assign::list_of()呢?要么_总是_将项目放在自己的命名空间中,要么_从不_这样做 - 这样你的用户在第一次尝试时就能直观地正确使用你的名称。 - wilhelmtell

2
在C ++中,结构体(struct)与类(class)完全相同,只是结构体默认为公共的,而类默认为私有的。因此,当您想要将自由函数分组在一起时,请使用命名空间。当您想要对数据和函数进行分组时,请使用结构体/类,并可选择在其周围使用命名空间。
请注意,如果您将函数放在结构体中,则在调用这些函数时必须拥有结构体实例,除非它们是静态的。

“当你想要将数据和函数分组时,请使用结构体/类,并可选择在其周围使用命名空间。”:“错误”。这并不是你在两者之间进行选择的方式。 - wilhelmtell
1
你会如何在两者之间做出选择? - J. Jaksche

2

当创建自己的库时,通常最好的做法是给所有导出的函数和类命名空间。

这样,如果有人包含了您的库,他们不会污染自己的命名空间,并且名称冲突的可能性较小。


2

这是一个反例,使用struct而不是namespace会带来一些意想不到的好处。

我想将一个2D问题的解决方案推广到K维。 2D解决方案被封装在一个命名空间中。

模板来拯救了。 我开始改变实现:

struct Point;

template< size_t DIMS >
struct Point;

我需要对大多数类、结构体和函数进行模板化。这是繁琐、重复且容易出错的。然后我有了这个恶作剧的想法。我改变了

  namespace KDimSpace {

  template< size_t DIMS >
  struct KDimSpace {

然后基本上就是这样了。我可以去掉所有template< size_t DIMS >里面的垃圾。这样更容易——维度的数量DIMS只声明一次,由所有类型和函数一致使用。

然后,还有另外一件事——而不是将实现的内部隐藏在::detail子命名空间后面,有public:private:

有两个烦恼:

  • 函数必须标记为静态
  • 不可能定义运算符(例如std::ostreamoperator<<),因为运算符不能被标记为静态(然后ADL也可能会产生问题)。

最后——C++语言可以更好,用较少的原语而不是更多的原语。我希望命名空间与结构体尽可能接近类。


1
这正是我一直在寻找的答案。然而,我发现了一个额外的好处,结构体中的函数可以互相调用,即使它们的定义顺序不一致,也不需要函数声明。而命名空间则要求在调用之前先进行前向声明或定义foo。示例代码:https://godbolt.org/z/s47jTPq4z - undefined
这正是我一直在寻找的答案。然而,我发现了一个额外的好处,结构体中的函数可以互相调用,即使它们的定义顺序不正确,也不需要函数声明。命名空间要求您在调用之前先前声明或定义foo。示例代码:https://godbolt.org/z/s47jTPq4z - Joseph Garvin

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