#pragma twice

KAB-studio > プログラミング > #pragma twice > 336 Version 16.09 const メンバ関数が呼べる・呼ばれる関数

#pragma twice 336 Version 16.09 const メンバ関数が呼べる・呼ばれる関数

前のページへ 表紙・目次へ 次のページへ

 Version 16.09
const メンバ関数が呼べる・呼ばれる関数

今回は const メンバ関数の続きです
世にも恐ろしい const メンバ関数の話ね
まず、 const メンバ関数の最も基本的な機能は、メンバ変数にアクセス
できなくなるということです

// m_iData の getter 。
int CData::GetData() const
{
    m_iData = 100;
    // コンパイルエラー:
    // error C2166: 左辺値は const オブジェクトに指定されています。

    return m_iData;
}

全部のメンバ変数が一時的に const になる、って感じ?
というよりは、 this ポインタが const になるって感じかな
 Version 16.03 ( No.330 ) の this ポインタ……そか、それが引数で渡
される時に、 const CData * になる、ってイメージすればいいわけね
そういうこと。実際、 this ポインタ経由でもコンパイルエラーになるか


    this->m_iData = 100;
    // コンパイルエラー:
    // error C2166: 左辺値は const オブジェクトに指定されています。

次に、非 const メンバ関数は、 const ポインタや const 参照から呼び
出すことはできません

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // CData クラスの変数を宣言します。
    CData cData;
    // const ポインタを作ります。
    const CData *pcData = &cData;
    // すると、非 const メンバ関数を呼び出せません。
    pcData->SetData( 100 );
    // コンパイルエラー:
    // error C2662: 
    // 'SetData' : 'const class CData' から 'class CData &' へ 
    // 'this' ポインタを変換できません。
    // 変換で修飾子が失われます。

    // const メンバ関数は呼び出せます。
    int i = pcData->GetData();

    return 0;
}

まず、 const ポインタを作り、対象の変数のアドレスを入れます

    // const ポインタを作ります。
    const CData *pcData = &cData;

すると、このポインタ経由で、非 const メンバ関数を呼び出すことはで
きません

    // すると、非 const メンバ関数を呼び出せません。
    pcData->SetData( 100 );
    // コンパイルエラー:
    // error C2662: 
    // 'SetData' : 'const class CData' から 'class CData &' へ 
    // 'this' ポインタを変換できません。
    // 変換で修飾子が失われます。

もしこれが呼べてしまうと、この非 const メンバ関数経由でメンバ変数
を書き換えられてしまうからです
つまり、前回のメンバ変数が書き換えられないのと同じ、ってことね
そういうこと。つまり、 const メンバ関数には以下の2つの機能があると
いうことです

・メンバ変数を書き換えられなくする
・const ポインタや const 参照から呼べるようにする

あれ? なんとなく違和感が……自分でもよく分からないんだけど……
たぶん、これが問題なんだと思うよ

・普通のメンバ関数(非 const メンバ関数)は、 const ポインタや
 const 参照からは呼び出せない

……普通のメンバ関数が呼び出せない!?
そう。普通のメンバ関数、つまり const じゃないメンバ関数は、 const 
ポインタや const 参照からは呼び出せません。ということは

・const メンバ関数を使ってないクラスは、 const ポインタや const 参照
 に渡せない

ということです
ええっ!? じゃあ、引数に const ポインタを使ったりできないってこ
と!?
そういうこと

void Set100( const CData *p_pcData )
{
    // セット……できません。
    p_pcData->SetData( 100 );
    // コンパイルエラー:
    // error C2662: 
    // 'SetData' : 'const class CData' から 'class CData &' へ 
    // 'this' ポインタを変換できません。
    // 変換で修飾子が失われます。
}

このように、引数に const ポインタを使うと、そのポインタから
非 const メンバ関数は呼び出せません
だよね
ここで呼んでいるのは SetData() だからある意味当然だけど、 GetData()
だって、 const を付けなかったら呼べないでしょ
う”、そっか、書き換えないって分かっていても、 const メンバ関数じゃ
なくちゃ、 const ポインタからは呼び出せない……
もし火美ちゃんが一からクラスを作る場合、とりあえずメンバ関数に const 
を付けなかったら
 const ポインタからメンバ関数呼べない、ってゆーか const ポインタや 
const 参照が使えない、ってことになるのかな
そうなるね。でも
そしたらメンバ関数経由で間違ってメンバ変数を書き換える可能性がある
……
つまり

・引数にポインタや参照を使いたいけどメンバ変数の書き換えはしたくない
→const ポインタや const 参照を使う
→const メンバ関数でないと呼び出せない
→クラスの各メンバ関数が適切に const 、非 const でなければならない
→それは大変だし難しい
→const ポインタや const 参照を使うのを諦める

っていうことになっちゃうんです
げげ……ってほどでもないんじゃない? だって、メンバ変数を書き換え
るメンバ関数以外は const にすればいいんじゃない
それはどうかな。今は簡単なメンバ関数だけだけど、複雑なメンバ関数の
場合、どこかで何かしらメンバ変数を書き換えるものでしょ
確かに、全然書き換えないメンバ関数って少ないかも……
で、ちょっとでも書き換えるメンバ関数は const メンバ関数にできない
から、 const ポインタや const 参照から呼び出せなくなります
げ! そか、そうやって const じゃないメンバ関数を増やすと、呼び出
せるメンバ関数がどんどん減ってっちゃうんだ
さらに、 const メンバ関数には、次のようなルールもあるんです

・const メンバ関数から非 const メンバ関数は呼び出せない

つまり、以下のようなことをするとコンパイルエラーになります

// m_iData の getter 。
int CData::GetData() const
{
    SetData( 100 );
    // コンパイルエラー:
    // error C2662: 
    // 'SetData' : 'const class CData' から 'class CData &' へ 
    // 'this' ポインタを変換できません。
    // 変換で修飾子が失われます。

    return m_iData;
}

このように、 const メンバ関数から非 const メンバ関数は呼べません。
呼べたら、 const メンバ関数がメンバ変数を書き換えられることになっ
ちゃうからね
ええっ! そか、これ呼べたら const メンバ関数の意味ないもんね……
中から中でも、こういう制約があるんだ
ということは、流れとしては

・  const メンバ関数 →非 const メンバ関数
・非 const メンバ関数 →非 const メンバ関数

この二通りの流れしかできないということになります
つまり逆の

・非 const メンバ関数 →  const メンバ関数

はないってこと……
もしクラス内で複雑にメンバ関数を呼び合っている場合、この 
const メンバ関数の流れを解決する必要があります。たとえば

・メンバ関数A→メンバ関数B→メンバ関数C

と呼んでいる場合、もし全てのメンバ関数でメンバ変数の書き換えをして
いないのなら
全部 const メンバ関数にできる!

・const メンバ関数A→const メンバ関数B→const メンバ関数C

ところが、仕様変更でメンバ関数Cではあるメンバ変数を書き換える必要
が出たとしたら?
え……メンバ関数Cは非 const メンバ関数になるね

・const メンバ関数A→const メンバ関数B×メンバ関数C

うあぁ、メンバ関数Bから呼べないよ!! ってことはメンバ関数Bも非
 const に……そしたらメンバ関数Aも!?
そうなると、メンバ関数Aは const ポインタや const 参照から呼び出せ
なくなります
うがーっ、何それわけわかんねー!! やめ! const ポインタとか
const メンバ関数とかもう使わねー!!

/*
    Preview Next Story!
*/
はぁはぁ……まさか const メンバ関数がここまで手強いとわっ!
思わず言っちゃうでしょ
使いたくねー!!
さて、次回はコンストラクタについて
へ?
というわけで次回
< Version 16.10 コンストラクタの復習 >
につづく!
気持ちを切り替えてがんばろう!
お、おー……?
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。