できるだけconstを使う

5363 ワード

できるだけconstを使う
const定義意味制約:変更されないオブジェクトを作成すると、コンパイラはこの制約を強制します.値が一定に保たれている限り、コンパイラはこの制約が違反しないことを確認することができます.
const多才多芸:classesの外部でglobalまたはnamespaceの役割ドメインの定数を修飾したり、ファイル、関数、ブロックの役割を修飾したりしてstaticとして宣言されたオブジェクトを修飾することができます.classes内部のstaticまたはnon-staticメンバー変数を修飾するために使用できます.ポインタに直面して、ポインタ自体、ポインタが指すもの、または両方がconstではないことを指摘することもできます.
char greet[] = "hello";
char *p = greet;            //      ,     
const char *p = greet;      //      ,    
char * const p = greet;     //     ,     
const char * const = greet; //     ,    

キーワードconstが*番の左側に表示された場合、被指物が定数であることを示します.*番号の右側に表示される場合は、ポインタ自体が定数であることを示します.*番の両側に表示される場合は、被指物とポインタの両方が定数であることを示します.
被指物が定数である場合は、キーワードconstをタイプの前に書くか、タイプの後、*番号の前に書くことができます.具体的には以下の通りです.
void f1(const Widget *pw);  // f1      ,       Widget  
void f1(Widget const *pw);  // f2      ,       Widget  

STL反復器は指針をもとに成形型から出てくるので,反復器の役割はT*指針である.宣言反復器がconstであることは、宣言ポインタがconstであるように(T*constを宣言する)、この反復器が異なるものを指していないことを示しますが、その指すものの値は変更できます.反復器が指すものを変更できないようにするには、const T*ポインタとして定義すればよい.例を次に示します.
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();  // iter T * const
*iter = 10;    //       
++iter;        //   ,iter const    

std::vector<int>::const_iterator cIter = vec.begin(); // cIter const T*
*cIter = 10;   //   ,*cIter cosnt
++cIter;       //   ,    cIter

constの最も威力のある使い方は、関数宣言に直面したときの応用です.1つの関数宣言式では、constは関数の戻り値、各パラメータ、関数自体(メンバー関数の場合)に関連付けられます.関数に定数値を返すと、セキュリティと効率性を放棄することなく、お客様のエラーによる予期せぬ事故を低減できます.良好なユーザーカスタマイズタイプの特徴は、組み込みタイプとの互換性を無端に回避することです.constパラメータは、local constオブジェクトのように、必要に応じて使用されます.パラメータまたはlocalオブジェクトを変更する必要がある場合を除き、constとして定義します.予期せぬ入力を防ぐことができます.例を次に示します.
class Rational{...};
const Rational operator* (const Rational &lhs, const Rational &rhs);

constメンバー関数
メンバー関数にconstを実装する目的は、そのメンバー関数がconstオブジェクトに作用することを確認するためです.このような関数の重要な原因:1)classインタフェースが比較的理解されやすい.どの関数がオブジェクトの内容を変えることができるか、どの関数ができないかを知ることは重要です.2)それらは操作constを可能にする.これはコードの効率化の鍵です.C++効率の根本的な方法はpass by reference-to-const方式でオブジェクトを伝達することである.ただし、constメンバー関数は、取得したconstオブジェクトを処理するために使用できることが前提です.
C++の重要な特性:2つのメンバー関数は定数だけが異なる場合、再ロードできます.例を次に示します.
class TextBlock{
public:
    const char &operator[](std::size_t position) const  // operator[] for const  
    { return text[position]; }
    char &operator[](std::size_t position)              // operator[] for non-const  
    { return text[position]; }
private:
    std::string text;
};
TextBlock operator[]s       :
TextBlock tb("hello");
std::cout << tb[0];     //   non-const TextBlock::operator[]
const TextBlock cbt("World");
std::cout << ctb[0];    //   const TextBlock::operator[]

実際のプログラムではconstオブジェクトの多くはpassed by pointer-to-constまたはpassed by reference-to-const伝達結果に使用されます.次の呼び出しを使用できます.
void print(const TextBlock& ctb)   // ctb const
{
    std::cout << cbt[0];   //  const TextBlock::operator[]
}

Open-const TextBlocksは、operator[]を再ロードし、異なるバージョンに異なる戻りタイプを与える限り、次の処理を実行できます.
std::cout << tb[0];    //   , non-const TextBlock
tb[0] = 'x';           //   , non-const TextBlock
std::cout << ctb[0];   //   , const TextBlock
ctb[0] = 'x';          //   , const TextBlock

上記のエラーの原因は、constバージョンのoperator戻り値に付与動作エラーを実施したためです.注意:non-const operator[]の戻りタイプはcharではなくreference to charです.charの場合、tb[0]=「x」はコンパイルできません.なぜなら、関数の戻りタイプは内蔵タイプであり、関数の戻り値を変更するのは合法的ではないからです.
bitwise const:「ポインタが指すもの」を変更したメンバー関数はconstとは言えませんが、ポインタ(そのポインタではなく)だけがオブジェクトに属している場合、bitwise constはコンパイラの異議を引き起こすことはありません.
logical const:constメンバー関数は、処理されるオブジェクト内のいくつかのbitsを変更できますが、クライアントが検出できない場合にのみ使用できます.
C++のconstに関連する揺動場を用いてmutable(可変)を用いた.mutableはnon-staticメンバー変数のbitwise constness制約を解放します.
constとnon-constメンバー関数での重複を避ける
non-constにconst兄弟を呼び出すのは、コードの重複を避ける安全な方法です.プロセス中に移行動作が必要であっても.例を次に示します.
class TextBlock{
public:
    const char &operator[](std::size_t position)const 
    {
        ...
        return text[position];
    }
    char& operator[](std::size_t position)   //      const op[]
    {
        //  op[]    const   *this  const  const op[]
        return const_cast<char&>(static_cast<const TextBlock&>(*this)[positon]);
    }
}

前例には2つの転換動作がある.*thisにconstを追加するのに初めて使用され、次にoperator[]を呼び出すとconstバージョンが呼び出されます.2回目はconst operator[]の戻り値からcosntが削除されます.コードの重複効果を回避できます.const operator[]を使用してnon-constバージョンを実現しました.**注意:**constメンバー関数は、オブジェクトの論理状態を変更しないことを約束しますが、non-constメンバー関数にはこの承諾はありません.constでnon-const関数を呼び出すと、変更しないと約束したオブジェクトが変更されるリスクがあります.constメンバー関数がnon-constメンバー関数を呼び出すのはエラーです.オブジェクトが変わる可能性があるからです.このようなコードをコンパイラに通すにはconst_を使用する必要があります.castは*thisのconstの性質を解放します.non-constメンバー関数は、もともとオブジェクトに対して任意の動作を行うことができるので、constメンバー関数を呼び出すことはリスクをもたらしません.
constは奇妙で尋常ではないものだ.ポインタと反復器では、ポインタ、反復器、referenceが指すオブジェクトにあります.関数パラメータと戻りタイプに;local変数に;メンバー関数には、林林林はいつもそろっていない.使用できるはずです.
注意:
1)constとして宣言すると、コンパイラが誤った使い方を検出するのに役立ちます.constは、任意の役割ドメイン内のオブジェクト、関数パラメータ、関数戻りタイプに適用しないでください.メンバー関数本体.
2)コンパイラはbitwise constnessを強制的に実装しますが、プログラムを作成するときは「概念的な定数性」(conceptual constness)を使用する必要があります.
3)constとnon-constメンバー関数が実質的に等価に実装されている場合、non-constバージョンにconstバージョンを呼び出すことでコードの重複を回避することができる.
参考文献:Effective C+,侯捷