在重构一些旧代码时,我已经删除了一些实际上应该是静态的公共方法,因为它们a)不对任何成员数据进行操作或调用任何其他成员函数和b)因为它们可能在其他地方证明是有用的
这让我想到了将“帮助”功能组合在一起的最佳方法。 Java / C#方式是使用一类带有私有构造函数的静态函数,例如:
class Helper
{
private:
Helper() { }
public:
static int HelperFunc1();
static int HelperFunc2();
};
但是,作为C ++,您还可以使用命名空间:
namespace Helper
{
int HelperFunc1();
int HelperFunc2();
}
在大多数情况下,我认为我更喜欢名称空间方法,但我想知道每种方法的优缺点。例如,如果使用类方法,是否会有任何开销?
答案 0 :(得分:37)
开销不是问题,命名空间虽然有一些优点
您可以使用命名空间别名来获得优势 (调试/发布,特定于平台的助手,......)
e.g。我做过像
这样的事情namespace LittleEndianHelper {
void Function();
}
namespace BigEndianHelper {
void Function();
}
#if powerpc
namespace Helper = BigEndianHelper;
#elif intel
namespace Helper = LittleEndianHelper;
#endif
答案 1 :(得分:11)
如果人们需要某种类型,可能会使用class
(或struct
)超过namespace
的情况,例如:
struct C {
static int f() { return 33; }
};
namespace N {
int f() { return 9; }
}
template<typename T>
int foo() {
return T::f();
}
int main() {
int ret = foo<C>();
//ret += foo<N>(); // compile error: N is a namespace
return ret;
}
答案 2 :(得分:6)
为了增加Pieter的出色响应,命名空间的另一个优点是你可以转发声明你放在其他地方命名空间中的东西,特别是结构......
//Header a.h
// Lots of big header files, spreading throughout your code
class foo
{
struct bar {/* ... */);
};
//header b.h
#include a.h // Required, no way around it, pulls in big headers
class b
{
//...
DoSomething(foo::bar);
};
使用命名空间......
//Header a.h
// Big header files
namespace foo
{
struct bar {/* ... */);
}
//header b.h
// Avoid include, instead forward declare
// (can put forward declares in a _fwd.h file)
namespace foo
{
struct bar;
}
class b
{
//...
// note that foo:bar must be passed by reference or pointer
void DoSomething(const foo::bar & o);
};
一旦你最终得到一个跨越数百个源文件的项目,转发声明会在小标题更改后对编译时间产生很大影响。
由于枚举错误,答案太好了,不能让它死掉(见评论)。我用结构替换了枚举(只能在C ++ 0x中转发声明,而不是今天的C ++)。
答案 3 :(得分:4)
使用命名空间的主要优点是你可以重新打开它并在以后添加更多东西,你不能用类来做。这使得这种方法更适合松散耦合的助手(例如,您可以为整个库创建一个Helpers名称空间,就像所有STL在:: std中一样)
类的主要优点是可以使用它将它嵌套在类中,不能在类中嵌套命名空间。这使得这种方法更适合紧密耦合的助手。
在类和命名空间中,它们不会有额外的开销。
答案 4 :(得分:3)
命名空间提供了Koenig查找的额外优势。使用帮助程序类可能会使代码更加冗长 - 通常需要在调用中包含帮助程序类名。
命名空间的另一个好处是可读性。对于类,您需要包含“Helper”之类的单词,以便稍后提醒您特定类不用于创建对象
在实践中,两者都没有开销。编译后,只使用名称修改不同。
答案 5 :(得分:3)
从How do you properly use namespaces in C++?复制/修剪/重写部分答案。
您可以使用“使用”来避免重复辅助函数的“前缀”。例如:
struct AAA
{
void makeSomething() ;
} ;
namespace BBB
{
void makeSomethingElse() ;
}
void willCompile()
{
AAA::makeSomething() ;
BBB::makeSomethingElse() ;
}
void willCompileAgain()
{
using BBB ;
makeSomethingElse() ; // This will call BBB::makeSomethingElse()
}
void WONT_COMPILE()
{
using AAA ; // ERROR : Won't compile
makeSomething() ; // ERROR : Won't compile
}
命名空间不仅仅是包。另一个例子可以在Bjarne Stroustrup的“The C ++ Programming Language”中找到。
在“特别版”的 8.2.8命名空间组合中,他描述了如何将两个名称空间AAA和BBB合并到另一个名为CCC的名称空间中。因此,CCC成为AAA和BBB的别名:
namespace AAA
{
void doSomething() ;
}
namespace BBB
{
void doSomethingElse() ;
}
namespace CCC
{
using namespace AAA ;
using namespace BBB ;
}
void doSomethingAgain()
{
CCC::doSomething() ;
CCC::doSomethingElse() ;
}
您甚至可以从不同的命名空间导入选择符号,以构建您自己的自定义命名空间界面。我还没有找到实际用途,但从理论上讲,它很酷。
答案 6 :(得分:2)
在创建辅助函数时,我倾向于使用匿名命名空间。因为它们(通常)只能被关心它们的模块看到,所以它是控制依赖关系的好方法。