简体   繁体   中英

c++ private static member variables compared to free nonmember


I'm relative new to C++ and have a question regarding private static member or "free nonmeber".

I can write my code like this:

// MyClass.h - Version 1
class MyClass
{
public:
    MyClass();
    ~MyClass();
private:
    static int iValue;
};

// MyClass.cpp
int MyClass::iValue = 123;

The other way without private member, with internal linking inside the cpp

// Version 2 - static 
static int iValue = 123;

// or anonymouse namespace
namespace
{
    int iValue2 = 123;
}

The only difference I can see is the "visibility" from outside of the class even though I can't use it.
If I want to use the iValue outside or in a derived class, I would declare the static member in Version 1 public or protected anyway or completely global above the class. It is even more unclear for me with the keyword const .

class MyClass
{
private:
    static const int iValue;
// (...)
}

I would always prefer to hide as many stuff from the outside as possible.

In short: Is there any reason why I should prefer Version 1 to Version 2?

Kind Regards
Nisi

Encapsulation is one of the main paradigms of object-oriented programming, so if your static member is used only by the class itself (eg to count the amount of class objects), you should use version 1 (private static member). Of course, if you want derived classes to be able to work with it, you should rather make it to protected .

Versions 2/3 (static global variable) should be used if the variable is not tied to any class in particular, and can be used in different places. However, one could argue that code that needs global variables is badly designed, using globals is considered a bad style.

Private static const member can be used if you need some constant value specific to a particular class (eg the number of wheels for a car).

Does MyClass (the class, not an instance) have an iValue ?

Yes => make it a static member.

No => do something else.

You are wondering what to do, which would indicate that there are no obvious tradeoff for you right now. This is a great indicator that you can and should Do the Right Thing ©, which is make it as simple as possible for others to understand your code, which in turn means : focus on the semantic meaning of your code.

Another point for the Do the Right Thing people : separation of concerns, your class should do one thing and one thing only. If iValue has nothing to do in the class, but you kind of would like it to be in some class somewhere : don't do it jimmy.

Design decisions in this space need to balance a few things, which are good to understand so you can weigh the pros and cons each time such a choice is needed:

  • Anonymous namespaces or static non-members allow all later code in the translation unit full access.

    • Used in an implementation files (eg ".cc" / ".cpp"), they let you have some static data/functions - not extern in the object's symbol table - which can't be accessed by code from other translation units.

      • Smaller symbol tables can sometimes reduce program linking and/or loading time.

      • This is generally deemed to have less encapsulation that a private member variable, as more than one class's code and friends can access/change the data (but definitions of class members may be spread across lots of translation units, and private data is more vulnerable to some types of "hacked" access such as client-provided template specialisations - nothing's ever clean-cut).

        • Less encapsulation's desirable if you want more of the code in the translation unit to have access: you're looking for a "good fit" to actual needs.
      • This reduces (re)compilation dependency (aka "physical dependency" in Lakosian terminology), meaning that you can change the data/functions without editing the header, and a recompile of just the translation unit lets the new data/behaviour be linked for incorporatation by however-many client objects/executables; that contrasts with edits to header files that tend to need recompilation and relinking of all the client code for incorporation. In enterprise-scale development that can be a big issue for changes in low-level libraries.

    • They're very rarely used in headers, because each translation unit that includes such a header would get an independent "copy" of the data/functions, and it's pretty rare for a program to want state stored per translation unit; that doesn't typically map cleanly onto logically meaningful divisions in the program data (eg per thread, per "user" at runtime), and even if a program could be structured so it appears useful it's generally better to manage multiple copies of data using object instances, keeping it uncoupled from translation units to preserve flexibility for later source code refactoring/restructuring.

    • They can't access any class's private/protected members, and there's a suggestion that they might unrelated to any specific class and potentially reusable, which - depending on how true that is - can aid or frustrate a developer's understanding when analysing the data usage or function behaviour/implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM