1

Possible duplicate: link

Hello everyone,

There's a weird thing that I don't understand about static member variables. If the "definition" (I'm not sure if it is the right word for it) for static variables are in the header file of a class, compiler gives linking errors however if they are in the cpp file, everything is OK.

I have a class like follows (didn't paste the entire thing):

UserInterface.h

class UserInterface
{
public:
    UserInterface(void);
    ~UserInterface(void);

    // Some method declarations here
private:
    // Some more methods declarations here
    // VARIABLES
    static bool                               m_undoRequested;
    static ChessViewConstants::MENU_STATE     m_displayState;
    static ChessModelConstants::PieceMovement m_pieceMovement;
};
// THESE DO NOT WORK (linking errors)
//bool UserInterface::m_undoRequested = false;
//ChessViewConstants::MENU_STATE UserInterface::m_displayState = ChessViewConstants::MAIN_MENU;
//ChessModelConstants::PieceMovement UserInterface::m_pieceMovement(1, 1, 1, 1);

UserInterface.cpp

#include "UserInterface.h"

// These do work.
bool UserInterface::m_undoRequested = false;
ChessViewConstants::MENU_STATE UserInterface::m_displayState = ChessViewConstants::MAIN_MENU;
ChessModelConstants::PieceMovement UserInterface::m_pieceMovement(1, 1, 1, 1);

// Implementation....

ChessConstants.h

namespace ChessModelConstats{
    // Some stuff here...

    struct PieceMovement {

    // A simple Constructor
    PieceMovement(int originRow = -1, int originCol = -1, 
                  int targetRow = -1, int targetCol = -1)
    : m_originRow(originRow), m_originCol(originCol),
      m_targetRow(targetRow), m_targetCol(targetCol) 
    {
    }

        // Members
        int m_originRow;
        int m_originCol;
        int m_targetRow;
        int m_targetCol;
    };

// More stuff here....
}

So why must the static variables be implemented inside the cpp file? Why can't I append at the end of the header file?

A second question: how can I initialize the struct variable (m_pieceMovement) like the follows:

m_pieceMovement.m_originCol = -1;
m_pieceMovement.m_originRow = -1;
m_pieceMovement.m_targetCol = -1;
m_pieceMovement.m_targetRow = -1;

It seems I'm missing a fundamental information here, don't be shy to throw in some novice tips here and there :)

Thanks in advance,

John John

EDIT: Here are the linking errors:

1>MasterController.obj : error LNK2005: "private: static bool UserInterface::m_undoRequested" (?m_undoRequested@UserInterface@@0_NA) already defined in Execution.obj 1>MasterController.obj : error LNK2005: "private: static enum ChessViewConstants::MENU_STATE UserInterface::m_displayState" (?m_displayState@UserInterface@@0W4MENU_STATE@ChessViewConstants@@A) already defined in Execution.obj 1>MasterController.obj : error LNK2005: "private: static struct ChessModelConstants::PieceMovement UserInterface::m_pieceMovement" (?m_pieceMovement@UserInterface@@0UPieceMovement@ChessModelConstants@@A) already defined in Execution.obj 1>UserInterface.obj : error LNK2005: "private: static bool UserInterface::m_undoRequested" (?m_undoRequested@UserInterface@@0_NA) already defined in Execution.obj 1>UserInterface.obj : error LNK2005: "private: static enum ChessViewConstants::MENU_STATE UserInterface::m_displayState" ?m_displayState@UserInterface@@0W4MENU_STATE@ChessViewConstants@@A) already defined in Execution.obj 1>UserInterface.obj : error LNK2005: "private: static struct ChessModelConstants::PieceMovement UserInterface::m_pieceMovement" (?m_pieceMovement@UserInterface@@0UPieceMovement@ChessModelConstants@@A) already defined in Execution.obj 1>D:\C++\CheatersChess\Debug\CheatersChess.exe : fatal error LNK1169: one or more multiply defined symbols found

Community
  • 1
  • 1
John John
  • 44
  • 7
  • Your second question is totally unclear. The structure has a default constructor moreover these data members are public so what is the problem with their initialization?! – Vlad from Moscow Jan 11 '14 at 13:05
  • If you have two questions, maybe you should ask two separate questions. – Alan Stokes Jan 11 '14 at 13:08
  • Well, there are cases when I want to declare static Object type member. I would want to initialize it with object->initialize() or something like that. Of course I would like to initialize it only once. Then how would I do that? I can't do it in a constructor, the only option seems to be making a `#define ObjectInitialized 1` kind of a thing which I would like to avoid. – John John Jan 11 '14 at 13:30
  • You can't call call a function like `object->initialize()` outside a function body. Therefore, for the static object definition, you can't use a member function like `initialize()`; you must do it in the constructor (but the constructor may call `initialize()` of course). If this is really absolutely impossible for you for some reason, then as a last resort, you can define a separate global object of a different type, and ensure that the constructor of that object calls the `initialize()` function of the first object. – jogojapan Jan 11 '14 at 13:37

1 Answers1

1

The C++ Standard includes a rule called the one-definition rule. Part of it is 3.2/3:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; [...]

When the static member of your question is defined in the header file, its definition will be included in every .obj file that is compiled from a .cpp file that includes that header. Since many files may include that header, you get multiple definitions, which is a violation of that rule. (Note that for this violation it does not matter whether all these definitions are identical.)

Whereas, when you place the definition in a .cpp file, the definition is included only in the .obj file that is compiled from that one .cpp file, causing no duplicate definitions when you link the program.

Regarding the second question: You need to define a constructor that takes the desired values for the members as arguments. You have in fact already done that. You can use that to define the static member (in the .cpp file):

ChessModelConstants::PieceMovement UserInterface::m_pieceMovement(-1,-1,-1,-1);
jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • Well the #ifndef guard is for multiple inclusions. And there's only a single class that includes the "UserInterface" class. I think I need further information regarding the linking process (I'm not expecting that from you ofc). Thanks for clarifying. – John John Jan 11 '14 at 13:28
  • The include guard doesn't help when multiple .cpp files include the same header and each of those .cpp files is compiled *separately*. The include-guards only help within one compilation process, not for multiple separate compilation processes. I.e., include-guards only take effect within a single translation unit. And from your error messages it seems that at least `MasterController.cpp` and `Execution.cpp` include `UserInterface.h`, and are compiled separately. – jogojapan Jan 11 '14 at 13:32
  • Well, `MasterController.h` includes `UserInterface.h` and `Execution.cpp` includes `MasterController.h` so.... I guess you're right. Since no-one will be including cpp files, I think it is safe to assume that static variables are declared in cpp files. But how would I do that with header only files (only class definitions and some simple method implementations)? Is there a way to do it with a header only class? – John John Jan 11 '14 at 13:39
  • Firstly, yes, indeed what I say above is only true when you don't start `#include`-ing .cpp files. Secondly, with a header-only class, you can't really have static members. The only exception are static members of integral or enum type, when they are not "odr-used", i.e. not used in the sense of the ODR (the one-definition rule). The exact definition of that (which I have not included above) is a bit complicated, but in any case, you will hardly find any practical use case where a variable that is never odr-used makes any sense. But there is another thing you can do: – jogojapan Jan 11 '14 at 13:44
  • (cont'd) Instead of the static member, you can define a static member *function* that defines a static variable in its body an returns that: `static int &myvalue() { static int val; return val; }` Then you can use `myvalue()` as if it was a static variable. There is no real downside to this, except that you have to add the parentheses where you use it. This works in a header-only library, and it works with non-integral types as well. – jogojapan Jan 11 '14 at 13:46
  • For completeness sake I should add that a `static` variable inside the function body *may* cause a minor performance drop, but it will be so minor that it will most certainly not matter unless you call that function excessively frequently. – jogojapan Jan 11 '14 at 13:49
  • Wow that has never came to my mind. Implementation-wise every time I need access to that static variable, it would cost me more in terms of performance since I'll have to make and remove a call stack, which is bad, but it is good that we can store a static variable this way. Great idea, thanks for sharing. – John John Jan 11 '14 at 13:50
  • The call-stack is not an issue because this very short function could be defined inside the class definition, which makes it an inline function. The compiler definitely inlines calls of that function so there would be no real call stack. It would be like using the variable directly. (The possible minor performance drop comes from the fact that with any static variable inside a function, the compiler has to ensure the variable is initialized only during the very first function call. This means it may put in some kind of `if`-statement to check if the variable has been initialized earlier.) – jogojapan Jan 11 '14 at 13:53
  • One more thing: Above I claimed that you will almost never use static member in a header-only scenario, because you will almost never have a variable that you never odr-use. There is actually one exception: If you have a `const` variable (of integral or enum type), and you never bind a reference to it, and never take its address, then you will most likely never have an odr-use. So a `const int` (or `const short` etc.), or a static `const` member of some `enum` type can make sense. And you can define that in the header (right at the point where you declare it, not on a separate line). – jogojapan Jan 11 '14 at 14:24