Cpp クラス const 新しいページはコチラ

提供: yonewiki
移動: 案内, 検索
 
1行: 1行:
 +
[[C PlusPlus#C++からの技術|C++]]に戻る
 +
 +
 
<table class="mbox-small" style="border:1px solid #aaa; background-color:#f9f9f9; width:22em;" id="RealTitleBanner">
 
<table class="mbox-small" style="border:1px solid #aaa; background-color:#f9f9f9; width:22em;" id="RealTitleBanner">
 
<tr>
 
<tr>
 
<td style="width:1px;"></td>
 
<td style="width:1px;"></td>
<td class="mbox-text plainlist" style="">本来の表記は「<b><span id="RealTitle" style="font-size:large;">C++ クラス const</span></b>」です。この記事に付けられた題名は{{記事名の制約}}から不正確なものとなっています。</td>
+
<td class="mbox-text plainlist" style="">本来の表記は「<b><span id="RealTitle" style="font-size:large;">C++(Cpp) クラス const</span></b>」です。この記事に付けられた題名は{{記事名の制約}}から不正確なものとなっています。</td>
 
</tr>
 
</tr>
 
</table>
 
</table>
8行: 11行:
 
<br />
 
<br />
 
== '''クラス const''' ==
 
== '''クラス const''' ==
 +
 
クラスとconstキーワードの関係について、この項目で記述したいと思います。変数で使っていたconstキーワードとはまた違った使い方をしますが、基本的な理念は同じです。つまりは変数の中に格納した値を初期化や初期値から変更されないように定数化・保護するということを目的としています。変えられたくない。変えられると動かなくなる。とか、変えられても大丈夫なようにチェックするということから解放されたい。こういったプログラマの都合により使われるキーワードです。
 
クラスとconstキーワードの関係について、この項目で記述したいと思います。変数で使っていたconstキーワードとはまた違った使い方をしますが、基本的な理念は同じです。つまりは変数の中に格納した値を初期化や初期値から変更されないように定数化・保護するということを目的としています。変えられたくない。変えられると動かなくなる。とか、変えられても大丈夫なようにチェックするということから解放されたい。こういったプログラマの都合により使われるキーワードです。
  
105行: 109行:
  
  
CConst001::CConst001(void):mpub_constnValue(0),m_constnValue(0),m_constnValue2(0)//★constメンバ変数の初期化 const以外も初期化できる。
+
CConst001::CConst001(void):mpub_constnValue(0),m_constnValue(0),m_constnValue2(0)//★10.constメンバ変数の初期化 const以外も初期化できる。
 
{
 
{
   //mpub_constnValue = 100;★コンストラクタでもconstメンバ変数への代入はできない。
+
   //mpub_constnValue = 100;★11.コンストラクタでもconstメンバ変数への代入はできない。
 
}
 
}
  
125行: 129行:
 
   CConst001* CConst001_Instance = new CConst001;//★5.
 
   CConst001* CConst001_Instance = new CConst001;//★5.
 
   CConst001_Instance->mfpub_Set_m_nValue(1000);
 
   CConst001_Instance->mfpub_Set_m_nValue(1000);
   //CConst001_Instance->mpub_constnValue = 1000;★constメンバ変数の呼び出しは出来ない。
+
   //CConst001_Instance->mpub_constnValue = 1000;★8.constメンバ変数の呼び出しは出来ない。
  
 
   const CConst001* CConst001_constInstance = new CConst001;//★6.
 
   const CConst001* CConst001_constInstance = new CConst001;//★6.
 
   CConst001_constInstance->mfpubconst_Get_m_nValue();
 
   CConst001_constInstance->mfpubconst_Get_m_nValue();
   //CConst001_constInstance->mfpub_Get_m_nValue();★const メンバ関数でないと呼べない。
+
   CConst001_constInstance->mfpub_Get_m_nValue();//★9.constメンバ関数以外も呼び出せる。
  
 
   return 0;
 
   return 0;
157行: 161行:
  
 
==constメンバ関数==
 
==constメンバ関数==
★2.★3.★4.の部分の内、★3.★4.のように関数名の最後にconstと記述するようなメンバ関数をconstメンバ関数と呼んでいます。メイン関数側の★6.部分のように呼び出し側のクラスオブジェクトそのものがすでにconstで定義されているか、どうかによって動作が変わる関数と考えていいと思います。★6.部分のようにconstで定義されている場合には★3.や★4.のようなconstメンバ関数として、メンバ関数内でメンバ変数を書き換えないことを保証した関数しか呼び出すことが出来ないオブジェクトになっています。★5.部分のようなconstで定義されていないオブジェクトであれば、★2.でも★3.でも★4.でも、つまりconstメンバ関数であっても、そうでなくても呼び出すことはできます。★2.と★4.のように同じ関数名でconstメンバ関数か、そうでない普通のメンバ関数かだけが違うような同名の関数を定義できます。同名の関数がある場合は、特にどちらを呼び出すかを指定しなくてもオブジェクトがconstであれば、constメンバ関数が選ばれて呼び出されます。通常のオブジェクトなら通常のメンバ変数が選ばれて呼び出されます。constにはconstを!constでなければconstでないものをという感じです。constメンバ関数がなければconstが定義されたオブジェクトからの呼び出しはエラーになります。もちろんconstメンバ関数内で★7.部分のようにメンバ変数を呼び出して、変数の中身を変更するような処理を記述することはできません。
+
★2.★3.★4.の部分の内、★3.★4.のように関数名の最後にconstと記述するようなメンバ関数をconstメンバ関数と呼んでいます。メイン関数側の★6.部分のように呼び出し側のクラスオブジェクトそのものがすでにconstで定義されているか、どうかによって動作が変わる関数と考えていいと思います。★6.部分のようにconstで定義されている場合には★3.や★4.のようなconstメンバ関数として、メンバ関数内でメンバ変数を書き換えないことを保証した関数しか呼び出すことが出来ないオブジェクトになっています。★5.部分のようなconstで定義されていないオブジェクトであれば、★2.でも★3.でも★4.でも、つまりconstメンバ関数であっても、★9.部分のように、そうでなくても呼び出すことはできます。★2.と★4.のように同じ関数名でconstメンバ関数か、そうでない普通のメンバ関数かだけが違うような同名の関数を定義できます。同名の関数がある場合は、特にどちらを呼び出すかを指定しなくてもオブジェクトがconstであれば、constメンバ関数が選ばれて呼び出されます。通常のオブジェクトなら通常のメンバ変数が選ばれて呼び出されます。このように自動的に利用される関数が振り分けられる仕組みを利用し、同じ関数名で二つ目の関数を定義することを関数のオーバーロードと呼びます。
 +
 
 +
 
 +
constにはconstを!constでなければconstでないものをという感じです。★8.部分のごとくです。
 +
 
 +
 
 +
constメンバ関数がなければconstが定義されたオブジェクトからの呼び出しはエラーになります。もちろんconstメンバ関数内で★7.部分のようにメンバ変数を呼び出して、変数の中身を変更するような処理を記述することはできません。
 +
 
 +
 
 +
プロトタイプ宣言(xxx.hファイル側の記述)をするときにもconstメンバ関数には、関数名の定義の後ろにconstのキーワードを記述する必要があります。
 +
 
 +
==constメンバ変数==
 +
constが定義されたクラス内のメンバ変数をconstメンバ変数と呼ぶような決まりは特にないですが、constが定義されたメンバ変数はコンストラクタの記述によって初期化をしなければ、初期値を与えることができません。初期値を与えるには★10.部分の方法でしか初期値を与えることはできません。★11.部分のようにコンストラクタ内で代入するような形式でも駄目だし、ヘッダファイル側のメンバ変数宣言のところで初期値を代入することでも初期値を与えることはできません。
 +
 
 +
 
 +
'''CConst001::CConst001(void):mpub_constnValue(0),m_constnValue(0),m_constnValue2(0)'''
 +
 
 +
 
 +
コンストラクタの関数の後に : を記述して、その後ろに引数を定義していきます。文字あるいは文字列のような一重引用符や二重引用符のリテラルを()の中に記述することもできます。この初期化は特にconstでないメンバ変数でなくても初期化できます。面倒ですが…。
 +
 
 +
 
 +
==const外し(const_cast) or const戻し==
 +
const_castを使うといろいろな場面でconstで指定されている変数(ポインタ変数やオブジェクトも含める)であったものをconst無しの扱いに変更が出来ます。それじゃ、constでは保護されないじゃん。なんじゃそりゃ。って思うやもしれませんが、constというものはそういうものです。では、どれくらいのことがやれるのか試してみましょう。
 +
 
 +
 
 +
===constポインタ変数→通常メンバ関数のポインタ変数(参照変数)引数===
 +
まず、メンバ関数の引数はconstではないのに、呼び出す側のプログラムではconstになってしまっている場合。これはメンバ関数を呼び出すときだけに限らず、通常のクラスではない関数の引数のときでも同じことなのですが、C++ではメンバ関数を多用しますので、基本的な作法として、一つ目の例としてもってきました。
 +
 
 +
例えばsample_main.cpp側で
 +
<syntaxhighlight lang="cpp">
 +
  …
 +
  省略
 +
  …
 +
int _tmain(int argc, _TCHAR* argv[])
 +
{
 +
  …
 +
  省略
 +
  …
 +
  const int* constpnValue = new int(2000);//★20.
 +
  …
 +
  省略
 +
  …
 +
}
 +
</syntaxhighlight>
 +
★20.部分のように値を変えられたくないint型のポインタ変数を実体化してconstに指定している場合で、以下に示すメンバ関数のようにint*型の引数を必要としている関数があった場合。
 +
 
 +
 
 +
Const001.h(メンバ関数追加)
 +
<syntaxhighlight lang="cpp">
 +
  …
 +
  省略
 +
  …
 +
class CConst001
 +
{
 +
  …
 +
  省略
 +
  …
 +
  public:
 +
    void mfpub_Set_m_nValueConstpPara(int* pnPara1);
 +
  …
 +
  省略
 +
  …
 +
}
 +
</syntaxhighlight>
 +
Const001.cpp(メンバ関数追加)
 +
<syntaxhighlight lang="cpp">
 +
  …
 +
  省略
 +
  …
 +
void CConst001::mfpub_Set_m_nValueConstpPara(int* pnPara1){
 +
  *pnPara1 = mpub_constnValue;
 +
  m_nValue = 1000;
 +
  printf("mfpub_Set_m_nValueConstpPara\n");
 +
  printf("m_nValue        =%d\n", m_nValue);
 +
  printf("mpub_constnValue=%d\n", mpub_constnValue);
 +
}
 +
  …
 +
  省略
 +
  …
 +
}
 +
</syntaxhighlight>
 +
のように引数の中身が書き換えられてしまう関数があった場合でそのまま、呼び出し側のconst変数を使わなければならないという、ありえないような状況に対応するならば、メイン関数の呼び出しは以下のようにcastすることで呼び出せてしまいます。
 +
 
 +
例えばsample_main.cpp側で
 +
<syntaxhighlight lang="cpp">
 +
int _tmain(int argc, _TCHAR* argv[])
 +
{
 +
  …
 +
  省略
 +
  …
 +
  CConst001* CConst001_Instance = new CConst001;
 +
  const int* constpnValue = new int(2000);
 +
  CConst001_Instance->mfpub_Set_m_nValueConstpPara(const_cast<int *>(constpnValue));//★21.
 +
  …
 +
  省略
 +
  …
 +
}
 +
</syntaxhighlight>
 +
★21.部分のように引数に
 +
 
 +
'''const_cast<int *>(constpnValue)'''
 +
 
 +
とします。
 +
 
 +
 
 +
ワ、ワイルドだろ?っていう、そんな感じですが、constな・ん・か、そんなの関係ねぇ~。そんなの関係ねぇ~。ハイ。おっぱっぴ~。こういう具合にconstの効力なんてのは、外そうと思えば外せるわけです。この場合はC++プログラムの理念から外れていることになります。今回の場合はメンバ関数内で書き換えられるわけだからメイン関数側でconstに指定したものを引数にしたことが間違っていることになります。メンバ関数の名前がSetって名前だから、本来は書き換えられるべきでない仕様のハズ。だとしたら、メンバ関数名がおかしいとも考えられるし、間違えて無意味に引数を書き換えてしまうようなメンバ関数になってしまっている可能性もあります。そうするとメンバ関数の引数側でconst int*という型にしておかなかったのが間違いだとも考えられます。
 +
 
 +
 
 +
つまり、const_castを使っているという事は何かの歯車が噛み合っていないそんな感じです。それでも、歯車が壊れない程度にガチャガチャ回し続けている社会というのは、よくあります。const_castはどんなことがあろうと、一日たりとも止めることが出来ない社会を成り立たせるために、法に触れない範囲で倫理的に許される範囲であるかどうかを判断しつつも、悪に手を染めてまで、誰かがドラマの悪役を演じて、その人が思う方向へ突きススメルためにある。そういうことです。
 +
 
 +
 
 +
以降でも、いくつかconst_castのパターンを記述していこうと思いますが、誰がどう悪いのかは説明しません。やろうと思えばできる。家のような大切な鍵でもキーインロックをやらかしてしまったら鍵の救急車なる謎の部隊を呼ぶことができて、簡単に施錠を解除してくれます。それで家の中に入れたとき、あぁ世界は素晴らしいって思えると思います。あぁC++は素晴らしい。あれ?なんか違うか?
 +
 
 +
 
 +
と、まぁうまいこと例えたつもりです。続きのパターンはまたぼちぼち書いていきます。
 +
 
 +
 
 +
===constメンバ関数内からメンバ関数呼び出し===
 +
constメンバ関数内から通常のメンバ関数を呼び出すことはできませんが、これもconst_castによって回避することができます。まずは、constメンバ関数内から通常のメンバ関数が呼び出せない動作について確かめるためのプログラムを作ってみます。
 +
 
 +
 
 +
サンプルは以下のとおりです。
 +
Const001.cpp(3つのメンバ関数の中身を変更しました。)
 +
<syntaxhighlight lang="cpp" line start="1">
 +
 +
省略
 +
 +
int CConst001::mfpubconst_Get_m_nValue() const{
 +
  int nValue = mfpub_Get_m_nValue();//★30.
 +
  printf("■mfpubconst_Get_m_nValue() const\n");
 +
  printf("nValue=%d\n",nValue);
 +
  return m_nValue;
 +
}
 +
 
 +
 
 +
int CConst001::mfpub_Get_m_nValue()      {
 +
  printf("■mfpub_Get_m_nValue()\n");
 +
  m_nValue = 10;★31.
 +
  return m_nValue;
 +
}
 +
 
 +
//★32.
 +
int CConst001::mfpub_Get_m_nValue() const{
 +
  printf("■mfpub_Get_m_nValue() const\n");
 +
  //m_nValue = 20;■constメンバ関数ではメンバ変数の変更は出来ない。
 +
  return m_nValue;
 +
}
 +
 +
省略
 +
 +
</syntaxhighlight>
 +
★30.部分でconstメンバ関数内から通常のメンバ関数を呼び出すようなつもりのプログラムを追記しました。
 +
 
 +
 
 +
それでsample_main.cppは変更せずに実行するとします。ちなみに、上記の変更について実際に呼び出しされる部分はこうでした。
 +
<syntaxhighlight lang="cpp" line start="1">
 +
 +
省略
 +
 +
int _tmain(int argc, _TCHAR* argv[])
 +
{
 +
 +
省略
 +
 +
  const CConst001* CConst001_constInstance = new CConst001;
 +
  CConst001_constInstance->mfpubconst_Get_m_nValue();
 +
 +
省略
 +
 +
}
 +
</syntaxhighlight>
 +
 
 +
そうすると、実行してみると以下のように出力されます。
 +
 
 +
実行結果
 +
<syntaxhighlight lang="text">
 +
■mfpubconst_Get_m_nValue() const
 +
■mfpub_Get_m_nValue() const
 +
nValue=-842150451
 +
</syntaxhighlight>
 +
という具合に、なります。★30.部分の記述では、動かないようなことを言っていたのに、あれ、呼び出せた?と一瞬思っちゃいますが、実はここでもconstにはconstを!の原理が働いています。
 +
 
 +
 
 +
'''int nValue = mfpub_Get_m_nValue();'''
 +
 
 +
 
 +
もし、mfpub_Get_m_nValue()による呼び出しができているとしたら、★31.が実行されてnValueの値は10になっているはずですが、出力ではまったく違う値になっています。実は予め用意していた、もう一方の★32.のconstメンバ関数が実行されています。これはconstオブジェクトではconstメンバ関数しか呼べないということの復習になるでしょうか?
 +
 
 +
 
 +
constメンバ関数が用意されていなければ、コンパイルエラーになります。
 +
 
 +
 
 +
で、★31が実行されるには、★30.の部分の呼び出しがconstではないオブジェクトから呼ばれたことにすればよいことになります。そのようにするには以下のように★30.部分を書き換えます。
 +
 
 +
 
 +
Const001.cpp
 +
<syntaxhighlight lang="cpp" line start="1">
 +
 +
省略
 +
 +
int CConst001::mfpubconst_Get_m_nValue() const{
 +
  int nValue = const_cast<CConst001*>(this)->mfpub_Get_m_nValue();//★40.
 +
  printf("■mfpubconst_Get_m_nValue() const\n");
 +
  printf("nValue=%d\n",nValue);
 +
  return m_nValue;
 +
}
 +
 +
省略
 +
 +
</syntaxhighlight>
 +
★40.部分のようにconst_castによって、クラスのthisポインタ変数の型をconst CConst001*型→CConst001*型に変換するようにすると、実行結果は以下のように変わります。
 +
 
 +
 
 +
実行結果
 +
<syntaxhighlight lang="text">
 +
■mfpubconst_Get_m_nValue() const
 +
■mfpub_Get_m_nValue()
 +
nValue=10
 +
</syntaxhighlight>
 +
になります。
 +
 
 +
 
 +
'''const_cast<CConst001*>(this)->mfpub_Get_m_nValue();'''
 +
 
 +
 +
 
 +
'''const_cast<const CConst001*>(this)->mfpub_Get_m_nValue();'''
 +
 
 +
 
 +
に変更すると、またconstオブジェクトに戻るので、実行結果も元に戻ります。thisポインタに関する理解がなければ、この項目のconst外しサンプルはイマイチわからないものに感じるかもしれません。thisポインタに関する理解があれば、他にも呼び出す方法はあると思いつく人もいるかもしれません。
 +
 
 +
 
 +
このことから、constとして生成されたオブジェクトからconstメンバ関数を呼ばれたときのメンバ関数内でのthisポインタの型はconst CConst001*のように constが付与されたポインタのように振る舞うことが分かります。
 +
 
 +
 
 +
長い文章になってしまいましたが、概ねの説明は完了です。
 +
 
 +
 
 +
[[C PlusPlus#C++からの技術|C++]]に戻る

2021年2月6日 (土) 00:00時点における最新版



個人用ツール
名前空間

変種
操作
案内
ツールボックス