它们应该是不会创建的

时间:2016-07-23 18:54:31

标签: c++ qt extern linkage

我目前正在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_YEARYEAR_COUNT已初始化,那么标题的其余部分也应该是,对吧? 这是我声明外部对象的地方:

date.h

//...Stuff
class Date
{
public:
    Date();

    Year& operator[](int);

private:
    std::array<Year, constants::YEAR_COUNT> date_arr;
} extern date;

2 个答案:

答案 0 :(得分:0)

date.cpp包含constants.h,其声明MONTH_DAY_MAPMONTH_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 ruleconstants.h定义在包含该文件的每个翻译单元中定义的变量。这些常量应该在匿名命名空间中定义,或者在标题中声明,但在.cpp文件中定义。整数常量可以声明为static:它们可以在常量表达式的上下文中使用,即使缺少定义。

全局变量将受到多个转换单元之间未定义的初始化顺序的影响。如果它们中的任何一个具有相互依赖性,即使是隐含的相互依赖性,您最终也会在某处使用未初始化的数据。所以不要这样做。相反,使用dependency injection,并考虑一个现代的轻量级依赖注入框架,例如, boost.DI

定义的位置与全局变量的声明之间存在不匹配。这不会导致未定义的行为,但开发人员的生活更加无缘无故。如果在task_vec中声明task.h,则应在task.cpp中对其进行定义。否则,您可能会完全混淆自己和任何从事项目工作的维护人员。