Every time I have some functionality which is in the direction of "utility", I end up wondering which option is the best. For instance, printing message structs (own or external), some encoding/decoding code or simply a few useful conversion functions in the context I'm working.
The options I think about are:
1) Static开发者_C百科 function in helper class/struct.
struct helper
{
static bool doSomething(...);
};
2) Nonmember function.
namespace helper
{
bool doSomething(...);
}
3) Static nonmember function.
namespace helper
{
static bool doSomething(...);
}
In some cases there might be necessary to initialize or keep state in the "utility", so then I go for option 1 to avoid "global" state. However, if there is no state that needs to be kept, should I then option 2 or 3? What's the practical difference between option 2 and 3?
What is important to consider and is there a preferred way to approach this? Thanks!
The difference between options 2 and 3 is that in the second case, the function will be internal to the translation unit. If the function is only defined in the cpp this should be the option (which is roughly equivalent to an unnamed namespace --which is a fourth option to consider, again roughly equivalent to 3).
If the function is to be used by different translation units then you should go with option 2. The function will be compiled once (unless you mark it as inline
and provide the definition in the header), while with option 3 the compiler will create it's an internal copy in each translation unit.
As of option 1, I would avoid it. In Java or C# you are forced to use classes everywhere, and you end up with utility classes when operations do not nicely map to the object paradigm. In C++ on the other hand, you can provide those operations as free standing functions, and there is no need to add the extra layer. If you opt for the utility class, don't forget to disable the creation of the objects.
Whether the functions are at class or namespace level will affect the lookup, and that will impact on user code. Static member functions need to be qualified with the class name always, unless you are inside the class scope, while there are different ways of bringing namespace functions into scope. As an illustrative example, consider a bunch of math helper functions, and calling code:
double do_maths( double x ) {
using namespace math;
return my_sqrt( x ) * cube_root(x);
}
// alternatively with an utility class:
double do_maths( double x ) {
return math::my_sqrt(x) * math::cube_root(x);
}
Which one you find easier to read is a different story, but I prefer the former: within the function I can select the namespace and then just focus on the operations and ignore lookup issues.
Don't use classes as namespaces. Using a free (ie. nonmember) function inside a namespace is the simplest thing.
Moreover, the function shouldn't be static if it has to be used across multiple translation units. Otherwise, it will be duplicated.
Using classes as namespace is stupid: why make a class which you won't instantiate ?
If some state is to be kept, then have a global state somewhere: static variable inside the function, utility global object (possibly in a "private" namespace, or as a static global variable in one of your translation units). Don't write classes you don't instantiate.
Alas, people with a C#/Java background are used to do this stupid thing, but it is because their language designers have decided unilaterally that free functions are evil. Whether they should be shot or not is a matter of religion.
As a last word of caution: global state should be rarely used. Indeed, it couples your code to the existence of the global state in a way you don't control when the code grows. You should always ask yourself why you don't make this coupling explicit.
I use struct with static functions, because it offers a slighly better isolation than non-member functions in namespaces (due to the avoidance of Koenig lookup).
To give an example of the thing I want to avoid:
namespace Foo
{
struct A
{
};
void f(const A& a) {}
}
void f(const Foo:A& a) { std::cout << "AAA"; }
int main(void)
{
Foo::A a;
f(a); // calls Foo:f
return 0;
}
精彩评论