2016-07-23 5 views
1

私は現在Qtで比較的小さなプロジェクトを作っています。プログラムの全期間を通じて利用できる2つのobjectと2 vectorがあります。そのために、対応するヘッダーファイルで4つの宣言を行い、externとマークしてMainWindow.cppで定義しました。ここで初めて使用します。
ただし、実行時エラーstd::out_of_rangeは、オブジェクトの1つが作成されているときに発生します。オブジェクトは作成されていません

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が初期化されている場合は、ヘッダの残りの部分も同様に正しくなければなりません。私はEXTERNオブジェクトを宣言のはここ
です:

date.h

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

    Year& operator[](int); 

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

答えて

0

date.cppMONTH_DAY_MAPMONTH_DAY_MAP_LYグローバルオブジェクトを宣言している、constants.hを含み;これらのグローバルオブジェクトはdate.cpp翻訳単位で定義されています。

mainwindow.cppは、4つのグローバルオブジェクトを宣言します。 Dateオブジェクトを構築します。 Dateのコンストラクタは、date.cpp翻訳単位のグローバルスコープのオブジェクトを参照するMonthのコンストラクタを呼び出します。

C++では、異なる翻訳単位のグローバルスコープオブジェクトの初期化の相対的な順序は指定されていません。異なる翻訳単位のグローバルオブジェクトは、実行時に任意の順序で初期化できます。

この場合、mainwindow.cppのグローバルスコープオブジェクトが構築される時点で、mainwindow.cppのグローバルスコープオブジェクトはまだ構築されていません。そのオブジェクトにアクセスすると、未定義の動作が発生し、クラッシュします。

これにはさまざまな解決策と対処方法があります。static initialization order fiascoです。あなたは、Googleで学び、学ぶべき多くの資料を見つけるべきです。

+0

私は、私はしばらくの間、このまわりで私の頭をラップすることができませんでしたけれども、私は、それを得たと思います。ご回答有難うございます! – 7Y3RPXK3ETDCNRDD

+0

標準では次のようになります。 名前空間スコープのオブジェクトの動的初期化(8.5、9.4、12.1、12.6.1)がmainの最初の文の前に行われるかどうかは、実装定義です。初期化がmainの最初のステートメントの後のある時点に延期されている場合、初期化されるオブジェクトと同じ翻訳単位で定義された関数またはオブジェクトの最初の使用の前に行われます。 '' Constants.h "のオブジェクトは、' 'Data''コンストラクタ呼び出しの前に初期化されなければなりません。 – 7Y3RPXK3ETDCNRDD

+0

"main()"の最初の文は "main()"を含む翻訳単位と同じではありません。さらに、これは「動的初期化」が 'main()'の前に出現する場合と出現しない場合があるが、異なる翻訳単位で宣言されたオブジェクトの動的初期化の相対的な初期化順序を指定するものではなく、 「初期化対象オブジェクトとしての翻訳単位」となる。 –

0

いくつかの問題があります。

あなたはone definition ruleに違反します。 constants.hは、そのファイルを含む各翻訳単位で定義される変数を定義します。これらの定数は、匿名の名前空間に定義するか、と宣言したと定義し、.cppに定義する必要があります。整数定数は静的であると宣言することができ、定義がない場合でも定数式のコンテキストで使用できます。

グローバル変数は、複数の翻訳単位間の未定義の初期化順序に苦しむだろう。それらのいずれかに相互依存性がある場合、暗黙的なものであっても、どこかで初期化されていないデータを使用することになります。そうしないでください。その代わりに、dependency injectionを使用して、例えば、以下のような最新の軽量依存性注入フレームワークを検討してください。 boost.DI

あなたはグローバル変数の定義と宣言の場所の間に不一致があります。これは未定義の動作につながることはありませんが、開発者の生活は何の理由もありません。 task_vectask.hに宣言されている場合、それはtask.cppで定義する必要があります。それ以外の場合は、あなた自身とあなたのプロジェクトに取り組んでいるメンテナが混乱する可能性があります。

関連する問題