C ++構造体を初期化する適切な方法


82

私たちのコードには、POD(Plain Old Datastructure)構造体が含まれています(これは、最初に初期化する必要がある他の構造体とPOD変数を含む基本的なc ++構造体です)。

私が読んだものに基づいて、それは次のようです:

myStruct = (MyStruct*)calloc(1, sizeof(MyStruct));

次のように、すべての値をゼロに初期化する必要があります。

myStruct = new MyStruct();

ただし、構造体が2番目の方法で初期化されると、Valgrindは後で、これらの変数が使用されたときに「条件付きジャンプまたは移動は初期化されていない値に依存する」と文句を言います。私の理解はここに欠陥がありますか、それともValgrindは誤検知を投げていますか?


1
また、new MyStruct()C ++ 03でパディングバイトを設定する必要がなかったことにも注意してください。C ++ 0xではそうです。C ++ 0xでは、パディングビットはすべて0に設定されます。
Johannes Schaub-litb

キャッチしてくれてありがとう、私は私の質問を編集しました。
shadow503


私は物事を理解しているので、そのエラーメッセージを受け取るべきではありません。おそらくあなたのデータ構造はPODではありませんか?コードを、この動作をまだ示している最小のプログラムに減らして、その蒸留されたプログラムを投稿できますか?(sscce.orgを参照してください)。
Robᵩ

どのコンパイラ?MyStructの定義を投稿できますか?
アランストークス

回答:


121

C ++では、クラス/構造体は(初期化に関して)同一です。

非POD構造体には、メンバーを初期化できるようにコンストラクターが含まれている場合もあります。
構造体がPODの場合は、初期化子を使用できます。

struct C
{
    int x; 
    int y;
};

C  c = {0}; // Zero initialize POD

または、デフォルトのコンストラクターを使用することもできます。

C  c = C();      // Zero initialize using default constructor
C  c{};          // Latest versions accept this syntax.
C* c = new C();  // Zero initialize a dynamically allocated object.

// Note the difference between the above and the initialize version of the constructor.
// Note: All above comments apply to POD structures.
C  c;            // members are random
C* c = new C;    // members are random (more officially undefined).

valgrindが不満を言っているのは、それがC ++の機能だったからだと思います。(C ++がゼロ初期化のデフォルト構造でいつアップグレードされたかは正確にはわかりません)。最善の策は、オブジェクトを初期化するコンストラクターを追加することです(構造体はコンストラクターとして許可されています)。

補足として:
多くの初心者はinitを評価しようとします:

C c(); // Unfortunately this is not a variable declaration.
C c{}; // This syntax was added to overcome this confusion.

// The correct way to do this is:
C c = C();

「MostVexingParse」をすばやく検索すると、私よりもわかりやすい説明が得られます。


1
C ++での変数のゼロ初期化に関するリファレンスはありますか?最後に聞いたところによると、それでもC. IIRCと同じ動作をします。MSVCコンパイラは、ある時点で初期化されていない初期化されていないメンバーを削除しました(パフォーマンス上の理由から)。これにより、他のMS部門にかなり深刻な問題が発生したようです:)
Marc Mutz-mmutz

1
いいえ、ですがC()あなたは正しいようです
Marc Mutz-mmutz

4
PODクラスでは、TT()常にゼロはスカラサブオブジェクトを初期化しています。トップレベルの初期化の名前は、C ++ 98ではC ++ 03とは異なりますが(「デフォルトの初期化」と「値の初期化」)、PODの最終的な効果は同じです。C ++ 98とC ++ 03はどちらも、すべての値がゼロになります。
Johannes Schaub-litb

1
C c();残念ながら、@ RockyRaccoonは変数宣言ではなく、関数の前方宣言です(パラメーターを受け取らず、タイプCのオブジェクトを返す「c」と呼ばれる関数)。グーグル:最も厄介なパース
マーティンヨーク

1
@RockyRaccoonこことコメントでリンクしている質問との違い。この場合、コンストラクターで使用されるパラメーターはありません。そのため、コンパイラはこれを解析して、関数の変数宣言と前方宣言の違いを判断するのに苦労しています。フォワード関数宣言を選択します。コンストラクターでパラメーターが必要な場合、コンパイラーはこれが問題なく変数宣言であることを確認できます。C c(4);有効な変数宣言です。
マーティンヨーク

2

あなたが私たちに言ったことから、それはvalgrindの誤検知であるように見えます。newwithの構文は()、オブジェクトがPODであると想定して、オブジェクトを値で初期化します。

構造体の一部が実際にはPODでなく、それが予期された初期化を妨げている可能性はありますか?コードを単純化して、valgrindエラーにフラグを立てる投稿可能な例にすることはできますか?

あるいは、コンパイラが実際にPOD構造を値初期化しない場合もあります。

いずれにせよ、おそらく最も簡単な解決策は、構造体/サブパーツの必要に応じてコンストラクターを作成することです。


1

私はいくつかのテストコードを書きます:

#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;

struct sc {
    int x;
    string y;
    int* z;
};

int main(int argc, char** argv)
{
   int* r = new int[128];
   for(int i = 0; i < 128; i++ ) {
        r[i] = i+32;
   }
   cout << r[100] << endl;
   delete r;

   sc* a = new sc;
   sc* aa = new sc[2];
   sc* b = new sc();
   sc* ba = new sc[2]();

   cout << "az:" << a->z << endl;
   cout << "bz:" << b->z << endl;
   cout << "a:" << a->x << " y" << a->y << "end" << endl;
   cout << "b:" << b->x << " y" << b->y <<  "end" <<endl;
   cout << "aa:" << aa->x << " y" << aa->y <<  "end" <<endl;
   cout << "ba:" << ba->x << " y" << ba->y <<  "end" <<endl;
}

g ++のコンパイルと実行:

./a.out 
132
az:0x2b0000002a
bz:0
a:854191480 yend
b:0 yend
aa:854190968 yend
ba:0 yend

わかりました、私はnew sc;vsにあまり精通していませんnew sc();。不足している親はエラーだと思っていたでしょう。しかし、これは(あなたがデフォルトのコンストラクタを使用する場合、それは0にはinitん)答えが正しいことを証明するようだ
nycynik

0

構造体にあるメンバーを初期化する必要があります。例:

struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

  public:
    MyStruct(): someInt_(0), someFloat_(1.0) {} // Initializer list will set appropriate values

};

この場合、私はクラスではなく構造体を使用しています。
shadow503

構造体はクラスです。私の投稿を編集しました:)
ralphtheninja

2
PODの場合はこれを行う必要はありません
Alan Stokes

0

これはPOD構造体であるため、いつでも0にmemsetできます。これは、フィールドを初期化する最も簡単な方法である可能性があります(適切であると想定)。


1
構造体であろうとクラスであろうと、memsetやコンストラクターを作成する機能とは何の関係もありません。たとえば、stackoverflow.com / questions / 2750270 / cc-struct-vs-class
Erik

コンストラクター(おそらく)が初期化を行ったため、通常、クラスの作成後にクラスをmemsetすることはありません。
スコットCウィルソン

1
デフォルトの保護(プライベートとパブリック、それぞれ)を除いて、C ++の「class」と「struct」の間に違いはありません。構造体は非PODにすることができ、クラスはPODにすることができ、これらの概念は直交しています。
マーク・ムッツ-mmutz

@mmutzと@ Erik-同意しました-混乱を防ぐために私の答えを短くしました。
スコットCウィルソン

memset()は最良のオプションではありません。デフォルトのctorを呼び出すだけで、POD構造体をゼロで初期化できます。
nils

0

それが私には最も簡単な方法のようです。構造体メンバーは、中括弧「{}」を使用して初期化できます。たとえば、以下は有効な初期化です。

struct Point 
{ 
   int x, y; 
};  

int main() 
{ 
   // A valid initialization. member x gets value 0 and y 
   // gets value 1.  The order of declaration is followed. 
   struct Point p1 = {0, 1};  
}

C ++の構造体に関する優れた情報があります-https ://www.geeksforgeeks.org/structures-in-cpp/

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.