我目前正在Qt开展一个相对较小的项目。在整个程序生命周期中,必须有2 object
和2 vector
s。为了实现这一点,我在相应的头文件中做了4个声明,将它们标记为extern并在MainWindow.cpp
中定义它们,我第一次使用它们。
但是,在创建其中一个对象时会发生运行时错误std::out_of_range
。经过长时间的调试后,我终于找到了错误的原因和来源:
MainWindow.cpp
#include "task.h" //Vectors; Works
#include "date.h" //Error
#include "db.h" //Works
std::vector<Task> task_vec; //extern from task.h
std::vector<Group> group_vec; //extern from task.h
Date date; //extern from date.h <- Error when instantinating this one
Database db; //extern from db.h
MainWindow::MainWindow(){//...}
//date and db objects are used in this file
date.cpp
#include "date.h" //it has "consants.h" included in it
//..Stuff
Date::Date()
{
//Use const int variable from "constants.h"
year = constants::START_YEAR; //Works, START_YEAR is initialized
year_count = constants::YEAR_COUNT //Works aswell
Month month(m, y);
}
Month::Month(int month, int year)
{
//Use const std::map<QString, std::pair<int,int>> from "constants.h"
day_count = constants::MONTH_DAY_MAP_LY.at(0).second //ERROR, MONTH_DAY_MAP_LY is not initialized
}
constants.h
namespace constants {
const int START_YEAR = 2016;
const int YEAR_COUNT = 83;
const QList<QString> MONTH { "January", "February", "March",
"April", "May", "June", "July", "August", "September", "October", "November", "December"};
const std::map<QString, std::pair<int, int>> MONTH_DAY_MAP{
{MONTH[0], std::make_pair(0, 31)}, {MONTH[1], std::make_pair(1, 28)}, {MONTH[2], std::make_pair(2, 31)},
{MONTH[3], std::make_pair(3, 30)}, {MONTH[4], std::make_pair(4, 31)}, {MONTH[5], std::make_pair(5, 30)},
{MONTH[6], std::make_pair(6, 31)}, {MONTH[7], std::make_pair(7, 31)}, {MONTH[8], std::make_pair(8, 30)},
{MONTH[9], std::make_pair(9, 31)}, {MONTH[10], std::make_pair(10, 30)}, {MONTH[11], std::make_pair(11, 31)}
};
const std::map<QString, std::pair<int, int>> MONTH_DAY_MAP_LY {
{MONTH[0], std::make_pair(0, 31)}, {MONTH[1], std::make_pair(1, 29)}, {MONTH[2], std::make_pair(2, 31)},
{MONTH[3], std::make_pair(3, 30)}, {MONTH[4], std::make_pair(4, 31)}, {MONTH[5], std::make_pair(5, 30)},
{MONTH[6], std::make_pair(6, 31)}, {MONTH[7], std::make_pair(7, 31)}, {MONTH[8], std::make_pair(8, 30)},
{MONTH[9], std::make_pair(9, 31)}, {MONTH[10], std::make_pair(10, 30)}, {MONTH[11], std::make_pair(11, 31)}
};
}
我不知道为什么。如果START_YEAR
和YEAR_COUNT
已初始化,那么标题的其余部分也应该是,对吧?
这是我声明外部对象的地方:
date.h
//...Stuff
class Date
{
public:
Date();
Year& operator[](int);
private:
std::array<Year, constants::YEAR_COUNT> date_arr;
} extern date;
答案 0 :(得分:0)
date.cpp
包含constants.h
,其声明MONTH_DAY_MAP
和MONTH_DAY_MAP_LY
个全局对象;因此,这些全局对象在date.cpp
翻译单元中定义。
mainwindow.cpp
声明它的四个全局对象。它构造了一个Date
对象。 Date
的构造函数调用Month
的构造函数,该构造函数引用date.cpp
翻译单元中的全局范围对象。
问题是C ++没有指定不同翻译单元中全局范围对象的初始化的相对顺序。来自不同翻译单元的全局对象可以在运行时以任何顺序初始化。
在这种情况下,在mainwindow.cpp
的全局范围对象构造时,mainwindow.cpp
的全局范围对象尚未构建,并且访问它们会导致未定义的行为和崩溃。
有各种解决方案以及处理此问题的方法static initialization order fiasco。你应该在谷歌找到大量可供学习和学习的资料。
答案 1 :(得分:0)
你有几个问题。
您违反了one definition rule。 constants.h
定义在包含该文件的每个翻译单元中定义的变量。这些常量应该在匿名命名空间中定义,或者在标题中声明,但在.cpp
文件中定义。整数常量可以声明为static:它们可以在常量表达式的上下文中使用,即使缺少定义。
全局变量将受到多个转换单元之间未定义的初始化顺序的影响。如果它们中的任何一个具有相互依赖性,即使是隐含的相互依赖性,您最终也会在某处使用未初始化的数据。所以不要这样做。相反,使用dependency injection,并考虑一个现代的轻量级依赖注入框架,例如, boost.DI
定义的位置与全局变量的声明之间存在不匹配。这不会导致未定义的行为,但开发人员的生活更加无缘无故。如果在task_vec
中声明task.h
,则应在task.cpp
中对其进行定义。否则,您可能会完全混淆自己和任何从事项目工作的维护人员。