C++ 静态结构类型成员初始化

Posted

技术标签:

【中文标题】C++ 静态结构类型成员初始化【英文标题】:C++ Static Struct Type Member Initializations 【发布时间】:2014-01-11 12:53:32 【问题描述】:

可能重复:link

大家好,

关于静态成员变量,我不明白一件奇怪的事情。如果静态变量的“定义”(我不确定它是否正确)在类的头文件中,编译器会给出链接错误,但是如果它们在 cpp 文件中,一切正常。

我有一个如下的课程(没有粘贴整个内容):

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....

那么为什么必须在cpp文件中实现静态变量呢?为什么我不能在头文件末尾追加?

第二个问题:如何初始化结构变量(m_pieceMovement),如下所示:

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

似乎我在这里遗漏了一些基本信息,不要害羞地在这里和那里抛出一些新手提示:)

提前致谢,

约翰·约翰

编辑:这里是链接错误:

1>MasterController.obj : 错误 LNK2005: "private: static bool UserInterface::m_undoRequested" (?m_undoRequested@UserInterface@@0_NA) 已在 Execution.obj 中定义 1>MasterController.obj : 错误 LNK2005: "private: static enum ChessViewConstants::MENU_STATE UserInterface::m_displayState" (?m_displayState@UserInterface@@0W4MENU_STATE@ChessViewConstants@@A) 已经在 Execution.obj 中定义 1>MasterController.obj : 错误 LNK2005: "private: static struct ChessModelConstants::PieceMovement UserInterface::m_pieceMovement" (?m_pieceMovement@UserInterface@@0UPieceMovement@ChessModelConstants@@A) 已经在 Execution.obj 中定义 1>UserInterface.obj : error LNK2005: "private: static bool UserInterface::m_undoRequested" (?m_undoRequested@UserInterface@@0_NA) 已经在 Execution.obj 中定义 1>UserInterface.obj : error LNK2005: "private: static enum ChessViewConstants::MENU_STATE UserInterface::m_displayState" ?m_displayState@UserInterface@@0W4MENU_STATE@ChessViewConstants@@A) 已经在 Execution.obj 中定义 1>UserInterface.obj : 错误 LNK2005: "private: static struct ChessModelConstants::PieceMovement UserInterface::m_pieceMovement" (?m_pieceMovement@UserInterface@@0UPieceMovement@ChessModelConstants@@A) 已经在 Execution.obj 中定义 1>D:\C++\CheatersChess\Debug\CheatersChess.exe : 致命错误 LNK1169: 找到一个或多个多重定义符号

【问题讨论】:

您的第二个问题完全不清楚。该结构有一个默认构造函数,而且这些数据成员是公共的,那么它们的初始化有什么问题?! 如果你有两个问题,也许你应该问两个不同的问题。 嗯,有些情况下我想声明静态对象类型成员。我想用 object->initialize() 或类似的东西来初始化它。当然我只想初始化一次。那我该怎么做呢?我不能在构造函数中做到这一点,唯一的选择似乎是制作 #define ObjectInitialized 1 那种我想避免的事情。 你不能在函数体之外调用像object->initialize()这样的函数。因此,对于静态对象的定义,不能使用initialize()这样的成员函数;您必须在构造函数中执行此操作(但构造函数当然可以调用initialize())。如果由于某种原因这对你来说真的绝对不可能,那么作为最后的手段,你可以定义一个不同类型的单独全局对象,并确保该对象的构造函数调用第一个对象的 initialize() 函数。 【参考方案1】:

C++ 标准包括一个称为单一定义规则的规则。部分是3.2/3:

每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的准确定义; [...]

当您的问题的静态成员在头文件中定义时,其定义将包含在从包含该头文件的 .cpp 文件编译的每个 .obj 文件中。由于许多文件可能包含该标头,因此您会获得多个定义,这违反了该规则。 (请注意,对于这种违规行为,所有这些定义是否相同并不重要。)

然而,当您将定义放在 .cpp 文件中时,该定义仅包含在从该 .cpp 文件编译的 .obj 文件中,因此在链接程序时不会导致重复定义。

关于第二个问题:您需要定义一个构造函数,该构造函数将成员的所需值作为参数。事实上,你已经这样做了。您可以使用它来定义静态成员(在 .cpp 文件中):

ChessModelConstants::PieceMovement UserInterface::m_pieceMovement(-1,-1,-1,-1);

【讨论】:

好吧,#ifndef 保护是用于多个包含的。并且只有一个类包含“UserInterface”类。我想我需要有关链接过程的更多信息(我不希望您提供)。感谢您的澄清。 当多个 .cpp 文件包含相同的标头并且每个 .cpp 文件被单独编译时,include 保护不起作用。 include-guards 仅在一个编译过程中有所帮助,对多个单独的编译过程没有帮助。即,include-guards 仅在单个翻译单元内生效。而且从你的错误信息看来至少MasterController.cppExecution.cpp包含UserInterface.h,并且是分开编译的。 嗯,MasterController.h 包括 UserInterface.hExecution.cpp 包括 MasterController.h 所以......我想你是对的。由于没有人会包含 cpp 文件,我认为可以安全地假设静态变量在 cpp 文件中声明。但是我将如何使用仅头文件(仅类定义和一些简单的方法实现)来做到这一点?有没有办法用一个只有标题的类来做到这一点? 首先,是的,我上面所说的确实只有当你不启动#include-ing .cpp 文件时才是正确的。其次,对于只有标题的类,你不能真正拥有静态成员。唯一的例外是整数或枚举类型的静态成员,当它们不是“使用 ODR”时,即没有在 ODR(单一定义规则)的意义上使用。它的确切定义(我在上面没有包括在内)有点复杂,但无论如何,您几乎找不到任何实际用例,其中从未使用过 odr 的变量有意义。但是您还可以做另一件事: (cont'd) 你可以定义一个静态成员 function 来代替静态成员,它在其主体中定义一个静态变量并返回:static int &myvalue() static int val; return val; 然后你可以像使用静态变量一样使用myvalue()。这并没有真正的缺点,只是您必须在使用它的地方添加括号。这适用于仅头文件的库,也适用于非整数类型。

以上是关于C++ 静态结构类型成员初始化的主要内容,如果未能解决你的问题,请参考以下文章

C++静态成员变量初始化

C++静态成员变量初始化

C++ 如何将一个静态结构体数组初始化?

C++中静态成员变量(不支持在类定义中初始化不是常量的静态数据成员)

C++ static 静态变量&静态成员函数

C++ 全局 静态结构体变量的初始化