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.cpp
和Execution.cpp
包含UserInterface.h
,并且是分开编译的。
嗯,MasterController.h
包括 UserInterface.h
和 Execution.cpp
包括 MasterController.h
所以......我想你是对的。由于没有人会包含 cpp 文件,我认为可以安全地假设静态变量在 cpp 文件中声明。但是我将如何使用仅头文件(仅类定义和一些简单的方法实现)来做到这一点?有没有办法用一个只有标题的类来做到这一点?
首先,是的,我上面所说的确实只有当你不启动#include
-ing .cpp 文件时才是正确的。其次,对于只有标题的类,你不能真正拥有静态成员。唯一的例外是整数或枚举类型的静态成员,当它们不是“使用 ODR”时,即没有在 ODR(单一定义规则)的意义上使用。它的确切定义(我在上面没有包括在内)有点复杂,但无论如何,您几乎找不到任何实际用例,其中从未使用过 odr 的变量有意义。但是您还可以做另一件事:
(cont'd) 你可以定义一个静态成员 function 来代替静态成员,它在其主体中定义一个静态变量并返回:static int &myvalue() static int val; return val;
然后你可以像使用静态变量一样使用myvalue()
。这并没有真正的缺点,只是您必须在使用它的地方添加括号。这适用于仅头文件的库,也适用于非整数类型。以上是关于C++ 静态结构类型成员初始化的主要内容,如果未能解决你的问题,请参考以下文章