【问题标题】:C++: "Class namespaces"? [duplicate]C++:“类命名空间”? [复制]
【发布时间】:2011-05-03 04:59:39
【问题描述】:

如果在 C++ 中我有一个类 longUnderstandableName。对于那个类,我有一个包含其方法声明的头文件。在类的源文件中,我必须写longUnderstandableName::MethodAlongUnderstandableName::MethodB等等,无处不在。

我能否以某种方式使用命名空间或其他东西,这样我就可以在类源文件中写入MethodAMethodB,并且只写在那里?

【问题讨论】:

  • 截至目前,答案是否定的。 但是...存在a proposal to add namespace class to the language,这将完全实现您和我想要的:在一个块中启用已声明的类成员的定义,该块将自动将它们限定为类 - 从而避免不断地重新输入类名范围、模板参数等。奇怪的是,它一直被采用到现在,这仍然只是在提案阶段,但我们到了。叹!我真的希望这可以使它成为 C++20 或其他任何东西。
  • @underscore_d 遗憾的是,该提案不包括 using class 等效项,因此它仍然不能提供完全的灵活性。
  • @negamartin 您还在寻找什么?请举个例子。重点是namespace class 将为课程作者解决此重复问题,这是 OP 提出此问题的角度。同时,该课程的用户可以执行using tiny_type = SomeNamespace::SomeOtherNamespace::AHugeClassName; 之类的操作。你还想要什么?
  • @underscore_d 想象一个类中的枚举,GiganticallyLongClassName::CONSTANT1GiganticallyLongClassName::CONSTANT2等等。如果类是命名空间,我可以在函数using GiganticallyLongNamespaceName;中使用,但是因为这是一门课,我每次都必须输入GiganticallyLongClassName::CONSTANT1。使用using class 指令,using class GiganticallyLongClassName; 之类的指令会将其缩短为 CONSTANT1CONSTANT2 等……我认为没有理由不将其包含在提案中。
  • @negamartin I have to type GiganticallyLongClassName::CONSTANT1 every time. 好吧,不是真的:正如许多其他人指出的那样(尽管我认为回答的问题与所问的问题不同......)你可以做到using g = GiganticallyLongClassName; g::CONSTANT1; - 但我明白你在说什么。我认为其他地方有关于您想要的 using 扩展类型的建议,我不确定 [ahem] 是否在 namespace class 的范围内。

标签: c++ oop class namespaces


【解决方案1】:
typedef longUnderstandableName sn;

然后你可以定义方法为

void sn::MethodA() {}
void sn::MethodB() {}

并将它们用作

sn::MethodA();
sn::MethodB();

这仅适用于longUnderstandableName 是类的名称。即使该类被深深嵌入到其他命名空间中,它也能正常工作。

如果longUnderstandableName是一个命名空间的名字,那么在你要使用方法的命名空间(或源文件)中,可以写

using namespace longUnderstandableName;

然后调用类似的方法

MethodA();
MethodB();

你应该注意不要在头文件中使用using namespace foo;,因为这样会污染我们#include头文件进入的每个.cpp文件,但是在@987654332的顶部使用using namespace foo; @ 文件绝对是允许和鼓励的。

【讨论】:

  • 真的有用吗?我的意思是 typedef 是制作缩写的救星,但声明通常需要原始名称。你用过这种方法吗?
  • @Basilevs:声明不需要原始名称。 typedef 不会“创建一个与旧类型不同的新类型”——它只是创建一个引用相同旧类型的新名称。人们一直使用这种方法将std::map<std::string,std::string>::iterator 之类的内容翻译成更短的内容。 (而 std::map<std::string,std::string>::iterator 通常已经是 typedef 用于在 STL 中作为实现细节的某些类。
  • 但这不是迭代器的声明。您引用的是在别处声明的类型迭代器的用法。
  • @Baslievs:看到某人typedef std::map<std::string,std::string>::iterator MapIt 是很常见的,这样他们就可以在循环中引用MapIt 类型并节省一些输入。在 C 中看到某人 typedef Foo* FooPtr 也很常见,虽然我不知道为什么人们认为这个 typedef 很有用。
  • @Basilevs 询问 typedef 是否可以在 定义 成员函数时使用,而不是在 使用 它们时使用,这是(我认为)原来的题。换句话说,下面的代码是否正确typedef longUnderstandableName sn; void sn::MethodA() {}? (显然是这样。)
【解决方案2】:

在类的方法中,你可以不加限定地使用名称,无论如何:只要去掉longUnderstandableName::前缀。

在类源文件中不是方法的函数中,我建议引入文件范围的静态内联函数,如下所示:

inline type MethodA(type param){
    return longUnderstandableName::MethodA(param);
}

那么就可以调用MethodA不合格了;由于内联性质,这可能不会产生任何运行时开销。

【讨论】:

  • 是的,但这会强制您在标头中定义方法,这是低效的,因为它会在类更改时强制重新编译所有客户端。
  • 不,它不会强迫你这样做。事实上,你不应该在头文件中定义方法,而是在类源文件的顶部 - 否则快捷方式将是全局可用的,这是 OP 不希望发生的。
  • 仅适用于静态成员。
  • @Basilevs:当然,但是无论如何,OP 显然是在谈论静态成员,因为例如,他通常根本不需要键入类名来调用方法。
  • @Martinv.Löwis 相当肯定的上下文表明他们在谈论不合时宜的定义方法,这总是需要详细的全名。
【解决方案3】:

我不确定我是否会推荐它,但您可以使用如下宏:

#define sn LongUnderstandableName

void sn::MethodA(parameters) { ... }
int sn::MethodB(parameters) { ... }

等等。宏的缺点之一是它们不尊重范围,但在这种情况下,您(显然)想要的范围是源文件,它恰好与宏的范围对应(非常接近)。

【讨论】:

  • +1,这是一个快速的解决方案,但我也不推荐它。这是灾难的秘诀。
  • 是的——我更有可能在编辑器中将它定义为一个宏,所以我只需要输入短名称,它就会自动扩展为真正的名称——但没有人否则必须意识到这一点。
  • 你可以使用 typedef。
  • -1 使 typedef 答案更好:)
  • 虽然您可以改用typedef,并且在某些时候/情况下typedef 提供了真正的优势,但这似乎成为其中之一。对于这种情况,typedef 是一个等效的答案,而不是更好的答案。
【解决方案4】:

嗯,是的,一旦你了解了命名空间。

不要将您的类命名为 MyBonnieLiesOverTheOcean,而是设置以下内容:

namespace My { namespace Bonnie { namespace LiesOverThe {
   class Ocean { ... };
} } }

现在,在定义方法时,您在整个文件周围放置相同的命名空间,然后编写:

Ocean::SomeMethod() ...

当使用所有命名空间之外的类时,它是:

My::Bonnie::LiesOverThe::Ocean

如果您需要在某个源文件中从其他命名空间中引用很多东西,您可以使用“use”指令来去掉前缀。

【讨论】:

  • 这是个好主意,但输入所有这些::) 一定很糟糕
  • 嗯,这就是我们有“使用”的原因。
  • 嗯,好的,是的。你可以说use My::Bonnie::LiesOverThe;然后在最后一个嵌套层工作
  • 如果你真的想要,你可以在一些源文件中使用namespace mbl = My::Bonnie::LiesOverThe;,并将类引用为mbl::Ocean
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-01
  • 2021-11-22
  • 2013-02-22
  • 2014-09-25
  • 2010-11-22
  • 2016-02-09
相关资源
最近更新 更多