構造体と共用体
C++の構造体
C++言語は、C言語と非常に互換性があります
すでにわかっているように、クラスの概念は構造体に酷似しています
実は、C++言語では構造体の機能が拡張されています
事実上、構造体はクラスと同じ扱いができるのです
C言語のメンバ変数の概念を拡張し、C++の構造体はメンバ関数を実装できます
ただし、構造体はデフォルトで公開メンバを持ちます
そのため、クラスとは異なりデフォルトでメンバは public なのです
逆に言うと、メンバがデフォルトで公開されていることを除いてクラスとまったく同じです
#include<iostream>
using std::cout;
struct Kitty {
explicit Kitty(char *str) { this->str = str; }
char * getStr() { return str; }
private:
char *str;
};
int main() {
Kitty obj("Kitty on your lap\n");
cout << obj.getStr();
return 0;
}
構造体 Kitty は、メンバ変数とメンバ関数を持ちます
構造体はデフォルトで公開なので、Kitty() コンストラクタと getStr メンバ関数は public です
なぜ、クラスがあるにもかかわらず、このような機能を持つのかというと
C++は、C言語との互換性を重視されて作られたからです
ついで、構造体の拡張によるデメリットが無いということもあります
じゃ、class キーワードが必要ないのではないかとも思いますが
class と struct は、ソース上で構文はまったくの別物になるので
クラスという機能の保守を行う時、Cの構造体との互換性を無視できるというメリットがあります
将来的にC++言語の仕様を変更する時、構造体だけではC言語という枠に縛られてしまいます
共用体
構造体が上記したように、機能が拡張されているということは
共用体にもメンバ関数を含めることができるのでしょうか?
実は予想どおり、共用体にもクラス同様にメンバ関数を含めることができます
もちろん、コンストラクタやデストラクタを持つことも可能です
しかし、共用体の場合は構造体と違って多くの制限を受けます
構造体とは異なり、共用体はメンバ関数を持てること以外は
基本的にC言語と同じで、新しい機能の導入は構造体に比べ消極的です
共用体は、C同様に全てのメンバ変数を同じメモリ領域に格納します
クラスではこのような機能はないため、C++で共用体は非常に重要な存在です
ただし、共用体は継承の機能がないため
基本クラスとして使ったり基本クラスを持つことがきません
また、仮想関数や静的データ メンバは収容できず
何らかのクラスのオブジェクト型のメンバ変数を保有することも許されません
#include<iostream>
using std::cout;
union Kitty {
Kitty(char ch) {
this->i = 0;
this->ch = ch;
}
int getInt() { return i; }
char getChar() { return ch; }
private:
int i;
char ch;
};
int main() {
Kitty obj('A');
cout << "ch = " << obj.getChar() << '\n';
cout << "i = " << obj.getInt();
return 0;
}
このプログラムの実行結果は次のようになります
ch = A
i = 65
オブジェクトがメンバ変数のメモリ領域を共有しているのがわかります
10進数 65 は、ASCIIコードで A に等しいです
無名共用体
C++には、無名共用体という特殊な共用体があります
無名共用体は、その名の通り名前を持たない共用体です
無名共用体は、基本データ型のメンバしか持たずメンバ関数を持てません
当然、メンバ関数がないので private や protected も指定できません
しかも、オブジェクトを持たずに通常の変数としてアクセスできる共用体です
もちろん、メンバ変数は共用体としての機能を保持しています
union {...}
無名共用体は、宣言したスコープと同じスコープレベルを持ちます
オブジェクトを持たず、直接メンバ変数にアクセスすることができるため
同じスコープレベルで、メンバ変数とローカル変数の名前の衝突は許されません
#include<iostream>
int main() {
union {
int i;
char ch;
};
i = 0; //初期化
ch = 'A';
std::cout << i;
return 0;
}
main() 関数内に無名共用体が作られています
無名共用対のメンバ変数 i と ch は、同一スコープ内では通常の変数と同じ方法でアクセスできます
ただし、グローバルな無名共用体はそのままでは宣言できません
グローバル無名共用体は必ず static を指定します
#include<iostream>
static union {
int i;
char ch;
};
int main() {
i = 0;
ch = 'A';
std::cout << i;
return 0;
}
グローバルな無名共用体に注目してください
グローバルな無名共用体の場合は static が必ず必要です
この無名共用体は、グローバル変数のようにアクセスすることができます
唯一違うのが、メンバは全て同じメモリを共有しているという点ですね