c++らしい構造体の定義と初期化
最近以下のC言語っぽい構造体の定義とその初期化をどうやってもっと C++ っぽくできるかっていう問題に出会いました。
typedef struct MyStruct
{
int a;
double b;
CustomType c;
} MyStruct;
MyStruct myStruct;
memset(&myStruct, 0, sizeof(MyStruct)); // メンバを0で初期化
構造体の定義
構造体の定義自体はC言語でもC++でも以下になります。
struct MyStruct
{
int a;
double b;
CustomType c;
};
でもこれだけだと、C言語の場合は、MyStruct
は構造体専用のネームスペースに追加されるだけなので、構造体のインスタンスを作る時に、以下のように毎回struct
を付けないといけなくなります。
struct MyStruct myStruct;
これを避けるために、以下のようにtypedef
を使って、MyStruct
をグローバルのネームスペースにも追加します。
typedef struct MyStruct MyStruct;
これを定義と一緒に書くと以下の定義になります。
typedef struct MyStruct
{
int a;
double b;
CustomType c;
} MyStruct;
C++の場合も、構造体専用のネームスペースも一応存在していますが、構造体専用のネームスペースに追加すると自動的にグローバルのネームスペースにも追加されるため、typedef
を使う必要がないです。
typedef
を使うと、既にグローバルのネームスペースに定義されているものを再定義することになるので、なるべく避けたいと思っています。ただ、typedef
で既に定義されている型に再定義することは許されています。(dcl.typedef)そのおかげで、上記のC言語の定義でも問題ないです。
つまりC++では、構造体の定義は以下で十分です。
struct MyStruct
{
int a;
double b;
CustomType c;
};
初期値で初期化
C言語では、以下のようにメンバー変数を1つずつ初期化できます。
MyStruct myStruct = { .a = 0, .b = 0, .c = 0 };
でもメンバー変数が多いせいで全部1つずつ指定したくない場合と、全部同じ値で初期化したい場合は、以下のmemset
での初期化をよく見かけます。
MyStruct myStruct;
memset(&myStruct, 0, sizeof(MyStruct));
これをC++っぽく書こうとしたくて、以下のように初期化しているのをたまに見かけます。
MyStruct myStruct{0};
MyStruct myStruct = {0};
確かにそれっぽく見えますが、これはmemset
の初期化と違う意味になります。
最初のメンバー変数だけが0
で初期化されて、他のメンバー変数はデフォルト初期値で初期化されます。double
型のb
のデフォルト初期値が0.0
なので、結果的に変わらないですが、CustomType
の初期値が0
以外の値の場合は、話が変わってきます。
memset
での初期化と同じことがしたい場合は、以下のように全メンバーに値を指定するしかないです。
MyStruct myStruct{0, 0, 0};
でもそもそもmemset
でメンバー変数の型をガン無視で構造体のメモリを0
で上書きするような強引な初期化のやり方自体がC++らしくないのです。
C++はカスタム型を自由自在に定義できるし、初期値も型によって違ったりするため、C++的に正しい初期化のやり方は以下になります。
MyStruct myStruct{};
これだと、全メンバーが各々のデフォルト初期値で初期化されます。これが一番C++らしい初期化です。
Discussion
いいえ。
名前空間が異なるから出来るのではなく、再定義を許すという規則によって許されます。
コメントありがとうございます。すみません元の文章では↓の2つの点があいまいに混ざった表現となっていたと思います。
もともと意識していたのは(1)の点のみで、指摘いただいた観点が不足していました。
リンクいただいた内容も踏まえて記事に反映してみました。
ありがとうございます。