C++プログラミングの基礎である共用体の実装をご紹介します。
共用体を使用することで、メモリの節約に貢献できます。
共用体とは
共用体とは様々な型のデータを同一のメモリ領域で管理する構造体のことです。
例えば以下のようなプログラムの場合、char型/short型/int型のデータは同じメモリ領域を使用することになります。
1 2 3 4 5 |
union data{ char data1; short data2; int data3; }; |
用途・どんな時に使えるの?
メモリ節約のためです。
組み込みシステムの場合、使用できるメモリ領域には限りがあり、型の違うデータをむやみに定義するのは良くありません。共用体を使用することで様々な型のデータを同じメモリ領域上で使用することができ、メモリ節約ができます。
また経験上、共用体を使用することで実装が楽になります。
例えば種類の違う構造体が3種類あり、1つの構造体として使用したい場合があります。その場合、共用体を使えば3種類の構造体を同じメモリ領域で使用することができるため、1つの構造体として使用することができます。
共用体を使用しない場合と使用した場合の比較
例えば以下のような1つのenumと3種類の構造体があります。
enumと各構造体をそれぞれ持ちたい場合、共用体を使用しない場合はEX_STRUCT_TYPE + EX_STRUCT_XXXを持つ構造体を新たに3つ定義する必要があります。
コードから分かるように、いちいち構造体を新たに定義するのは冗長であり、メモリを無駄に消費することになります。
■ 共用体を使用しない場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// enum enum EX_STRUCT_TYPE { EX_STRUCT_TYPE_INT, EX_STRUCT_TYPE_CHAR_P, EX_STRUCT_TYPE_STRING, }; /* 型の異なるメンバを持つ構造体 */ struct EX_STRUCT_INT { int data; }; struct EX_STRUCT_CHAR_P { char* data; }; struct EX_STRUCT_STRING { string data; }; /* EX_STRUCT_TYPE + EX_STRUCT_XXXを持つ構造体を新たに3つ定義しなければいけない */ struct EX_STRUCT_TYPE_INT { EX_STRUCT_TYPE type; EX_STRUCT_INT data_int; }; struct EX_STRUCT_TYPE_CHAR_P { EX_STRUCT_TYPE type; EX_STRUCT_CHAR_P data_char_p; }; struct EX_STRUCT_TYPE_STRING { EX_STRUCT_TYPE type; EX_STRUCT_STRING* data_string; }; |
共用体を使用することでこれらの問題が解決されます。
共用体の実装例は以下の通りです。
■ 共用体を使用する場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// enum enum EX_STRUCT_TYPE { EX_STRUCT_TYPE_INT, EX_STRUCT_TYPE_CHAR_P, EX_STRUCT_TYPE_STRING, }; struct EX_STRUCT_INT { int data; }; struct EX_STRUCT_CHAR_P { char* data; }; struct EX_STRUCT_STRING { string data; }; /* 共用体を持った構造体を定義. 共用体を使用すればEX_STRUCT_XXX毎に構造体を定義する必要がなくなる。 */ struct EX_STRUCT { EX_STRUCT_TYPE type; union { EX_STRUCT_INT data_int; EX_STRUCT_CHAR_P data_char_p; EX_STRUCT_STRING* data_string; } union_pram; }; |
実装の解説
C++で実装したサンプルコードを解説します。
このサンプルコードは共用体を使って型の異なるメンバを持つそれぞれの構造体を1つにまとめて使用します。
EX_STRUCT構造体でEnumであるEX_STRUCT_TYPEと共用体であるunion_pramを定義します。
EX_STRUCT_INT、EX_STRUCT_CHAR_P、EX_STRUCT_STRING構造体を共用体として扱うことで同じメモリ領域を使用することができ、使用する際はEX_STRUCT構造体として使用することができます。
使用する構造体が1つになることで実装上構造体の型を間違える心配がなくなります。
また共用体で持つ構造体が増えても、使用する構造体は変わりませんのでコードの変更量が少なくなり拡張性が良くなります。
共用体の注意点として、同じメモリ領域を使用しているため後にデータを設定すると前に設定したデータを上書きしてしまいます。設定したデータを全て管理したい場合は、共用体の配列を定義して管理することをお勧めします。サンプルコードではvector配列で管理しています。
■ ExUnion.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// ExUnion.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include "request.h" #include "logout.h" #include <string> #define MAX_SIZE (10) using namespace std; using namespace hukusuke; int main(void) { LOG_OUTPUT("Start."); // データ定義. int value_int = 0; char* value_char_p = new char[MAX_SIZE+1]; memset(value_char_p, 0, MAX_SIZE+1); strncpy(value_char_p, "CHAR_P", MAX_SIZE); string value_string = "STRING"; // リクエスト. request request_instance; request_instance.request_if(value_int); request_instance.request_if(value_char_p); request_instance.request_if(value_string); request_instance.output(); // 後処理. if (NULL != value_char_p) { delete value_char_p; value_char_p = NULL; } LOG_OUTPUT("Terminate."); return 0; } |
■ request.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
#include "request.h" #include "string.h" #include "logout.h" using namespace std; namespace hukusuke { void request::request_if(const int data) { // 共用体へセット. EX_STRUCT info; info.type = EX_STRUCT_TYPE_INT; info.union_pram.data_int.data = data; LOG_OUTPUT("EX_STRUCT_TYPE_INT: %d", info.union_pram.data_int.data); vec_data_.push_back(info); return; } void request::request_if(char* data) { if (NULL != data) { // 共用体へセット. EX_STRUCT info; info.type = EX_STRUCT_TYPE_CHAR_P; info.union_pram.data_char_p.data = data; LOG_OUTPUT("EX_STRUCT_TYPE_CHAR_P: %s", info.union_pram.data_char_p.data); vec_data_.push_back(info); } return; } void request::request_if(const string data) { // 共用体へセット. EX_STRUCT info; info.type = EX_STRUCT_TYPE_STRING; info.union_pram.data_string = new EX_STRUCT_STRING; if (NULL != info.union_pram.data_string) { info.union_pram.data_string->data = data; LOG_OUTPUT("EX_STRUCT_TYPE_STRING: %s", info.union_pram.data_string->data.c_str()); vec_data_.push_back(info); } return; } void request::output() { LOG_OUTPUT("ALL_OUTPUT_UNION"); // セットされた共用体のメンバを全て出力. for ( vector<EX_STRUCT>::iterator iter = vec_data_.begin(); iter != vec_data_.end(); ++iter) { switch (iter->type) { case EX_STRUCT_TYPE_INT: LOG_OUTPUT(" + EX_STRUCT_TYPE_INT: %d", iter->union_pram.data_int.data); break; case EX_STRUCT_TYPE_CHAR_P: LOG_OUTPUT(" + EX_STRUCT_TYPE_CHAR_P: %s", iter->union_pram.data_char_p.data); break; case EX_STRUCT_TYPE_STRING: LOG_OUTPUT(" + EX_STRUCT_TYPE_STRING: %s", iter->union_pram.data_string->data.c_str()); break; default: break; } } return; } } |
■ request.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#pragma once #include <vector> #include <string> using namespace std; namespace hukusuke { class request { public: request() {}; ~request() {}; void request_if(const int data ); void request_if(char* data ); void request_if(const string data ); void output(); private: enum EX_STRUCT_TYPE { EX_STRUCT_TYPE_INT, EX_STRUCT_TYPE_CHAR_P, EX_STRUCT_TYPE_STRING, }; struct EX_STRUCT_INT { int data; }; struct EX_STRUCT_CHAR_P { char* data; }; struct EX_STRUCT_STRING { string data; }; /* 共用体を持った構造体を定義. 共用体を使用すればEX_STRUCT_XXX毎に構造体を定義する必要がなくなる。 */ struct EX_STRUCT { EX_STRUCT_TYPE type; union { EX_STRUCT_INT data_int; EX_STRUCT_CHAR_P data_char_p; EX_STRUCT_STRING* data_string; } union_pram; }; vector<EX_STRUCT> vec_data_; // 共有体を持つ構造体をvectorで定義. }; } |
サンプルコード
Visual C++ 2017で作成したサンプルコードをGitHubで公開しています。
GitHub – ExUnion
実行結果は以下の通りです。
設定前後のデータで相異無いことが確認できます。
[ 15] Start. [ 15] EX_STRUCT_TYPE_INT: 0 [ 29] EX_STRUCT_TYPE_CHAR_P: CHAR_P [ 44] EX_STRUCT_TYPE_STRING: STRING [ 53] ALL_OUTPUT_UNION [ 58] + EX_STRUCT_TYPE_INT: 0 [ 61] + EX_STRUCT_TYPE_CHAR_P: CHAR_P [ 64] + EX_STRUCT_TYPE_STRING: STRING [ 34] Terminate.
参考
以下のサイトを参考にさせていただきました。
共用体 – ITエンジニアのための技術支援サイト
新品価格 |