const浅析


文書ディレクトリ
  • 前言
  • const
  • 1. constについて:
  • 2. constオブジェクト初期化
  • 3. 最上位および最下位constコンセプト
  • 4. constとリロード関数
  • 5. constとクラスの定数メンバー関数
  • 6. constとクラス
  • 7. constとクラス静的メンバー
  • 8. constとtypedef
  • まとめ
  • 前言
    c++においてconstに使用する箇所は多く、const自体もポインタに対して最上位層と最下層層があるなど、異なるタイプに対して異なる意味を持つ可能性がある.本節はC++におけるconstの異なる場所での異なる表現または意味について検討する.
    const const修飾オブジェクトは、定義時に初期化する必要があります.
    1.constについて:
  • const修飾オブジェクトは、作成すると一般的には変更できないため、constオブジェクトに対して初期化が必要となる.
    int i = 0;
    const int j; 	// error.        
    const int j = 0;
    
  • 初期化時に初期化対象がconstであるか非constであるかに関心がない
    int i = 0;
    const int j = i;	// i   const   
    
  • .
  • const const
    const int i = 0;
    i = 1; 	// error const           
    
  • は変更できません.
  • 参照オブジェクトのタイプは、参照オブジェクトのタイプと一致する必要があります
    int i = 0;
    int &j = i;
    double &size = j; // error. size j      
    
  • 上記参照のルールのため、constのタイプの参照はdouble j = size;のオブジェクト参照
  • のみである.
    int i = 0;
    const int &size = i;
    int &j = size; // error. size    const int, j     int.       
    
  • 参照タイプに対応する例外
    int size = 0;
    const double &i = size; // size i        ,     const         
    
    理由:iとsizeの両方のタイプが一致しないが、iを初期化するとコンパイラはsizeに一時量(const double &i = j ))を生成し、そして、iが最終的にこの一時量にバインドする(const.iが一時量にバインドできるのは、constのオブジェクトが修正できないためであり、iは修正できず、一時量が変更されないことを保障する.実際に一時量にバインドされ、sizeにバインドされていないことに注意する.
  • 修正constのオブジェクトの値
    int size = 0;
    const double &i = size;
    size = 1;	// i         ,           size
    
    修飾のオブジェクトに対してのみ修正できないことを制限するため、オブジェクトが一定量であることを保証することはできないので、定数であることを保証できるオブジェクトはconstexprと定義することが望ましい.constexprに対してはっきりしないのはconstexprの浅い分析
  • を見ることができます
    2.constオブジェクトの初期化constのオブジェクト像非constのオブジェクトを用いる初期化を行う際には、参照やポインタのように初期化オブジェクトの種類が要求されることに注意が必要である.
  • 非参照ポインタの一般的なタイプは、付与動作
    int i = 0;
    const int size = i;
    const int &j = i;
    const_cast<int&>(size) = 1;	//  size     1
    i = 2;	//   j  i, i    j    
    
    を直接行うことができる
  • である.
  • 参照およびポインタ参照およびポインタは、同じタイプのオブジェクト
    const int i = 0;
    int j = i;
    
  • のみを初期化する.
    3.トップレベルと下位レベルのconst概念
    最上位const:ポインタ自体が定数である(すなわち、アドレスの変更は許されない).
    実は私たちはずっと最上階のconstを使っています.例えばint i = 0;です.これは最上階のconstです.iのアドレスは変わらないので、値だけが変わります.
    const int ci = 0;
    const int *pi = &ci;
    
    int &r = ci;	// error
    int *p = pi;	// error
    

    覚えておいてください:const int i = 0、ここのiも const*です.
    下位const:ポインタが指すオブジェクトは定数である(ポインタ自体は修正可能であるが、指す値だけは修正できない).
    int size = 0, i = 0;	//      const
    int *const p = &size; // const        ,   const
    p = &i;		// error. p   const
    *p = 1;		//   const       
    

    もちろん、オブジェクトを最上位レベルと最下位レベルに変更できます.
    int size = 0, i = 0;
    const int * p = &size; // const           ,   const
    ptr = &i;	// ptr          ,      const
    *ptr = 1;	// error.   const          
    

    最上位constがコピーされると、最上位constは無視されることに注意してください.
    int size = 0;
    const int * const p = &size;	//         
    const int i = 0;	//         
    

    4.constとリロード関数
    関数を再ロードする場合、const型のパラメータは、最上位constと最下位constを処理する際に問題が発生する可能性がある.具体的にどんな問題を分析してからまとめますか.
    const int i = 0;
    int size = i; 	//      const     
    auto j = i; 	//    auto     j     int , i    const    
    

    上から分かるように、最上位constは無視されるため、最上位constと他の最上位constは区別できない.
    void Ccount(int ccount) {}			// ccount   const
    void Ccount(const int ccount) {}	// error. ccount     const,          const,           
    
    void Ccount_pointer(int *ccount) {}	// ccount   const
    void Ccount_pointer(int *const ccount) {}	// error. ccount     const,          const,           
    

    参照対象のタイプは同一である必要があるため、int &iconst int &iと区別する、前者のタイプはint、後者のタイプはconst intであるため、後者は下位constである.
    下のconstは無視されず、下のconstは下のconstと区別されるため、下のconstはリロードに用いることができる.
    5.constとクラスの定数メンバー関数constを関数名の前に置くとその意味はコンパイラの戻りタイプがconstの定数であることを伝えるだけであるが、constを関数名の後に置くとまた別の状況であり、ここで主に分析するのは状況である.
    // error.             ,             ,        . 
    void const_reference(int i) {}	// i    const.       int
    void const_reference(int &i) {}	// i    const.       int
    
    //        
    void const_reference(int &i) {}	// i    const.       int
    void const_reference(const int &i) {}	// i    const.      const int
    
    void const_pointer(int *i) {}		//   const
    void const_pointer(const int *i) {}	//   const
    

    単純なクラスを定義する
    const int const_func(int i) {return i;}	//         const   ,    
    int const_func(int i) const {return i;}	// error. const              ,         (   )    ,      .
    
    int const_func() constの関数のconstはコンパイラに教えるものであり、クラスで定義非静的変数は変更できない.なぜなら、クラスのすべてのメンバー関数がthis に暗黙的に入力され、上記のメンバー関数が
    class A {
        private: int nun;
        public: int const_func() const {return 0;}	// success
    };
    //        
    int const_func() const { ++num; return 0;}	// error
    
    thisのポインタ自体が最上位のconstである、関数名の後ろに置かれたconstはthisポインタを修飾するためのものであるが、thisポインタが表示することができないため、constは関数名の後にしか置かない.
    ここでconstが修飾this であることが分かるので、this->iは修正することができず、静的メンバはインスタンス化クラスそのものではなく、thisが静的変数を指すこともないので、以上のタイプの関数で静的変数を修正することができる.constメンバー関数名の後ろに置かれた関数定数メンバー関数と呼びます
    しかし、上記の関数で変数の値を変更しなければならない場合があります.どうすればいいですか?c++にはmutableのキーワードがあり、このような特例の発生を許容する.mutableはコンパイラに教えるものであり、numは任意の関数で修正することができる.
    int const_func(const A * const this)  { ++num; return 0;}
    

    6.constとクラス
    クラスのインスタンス化を定義するときに、クラスのインスタンス化をconst、すなわち
    class A {
        private: mutable int nun;
        public: void const_func() const {++num;}	// success
    };
    

    上記のエラーの原因は、caのタイプがconstであるため、それに対応する関数は定数メンバー関数であるべきであるため、クラス関数の実装を定義する際に、定数メンバー関数を1つ再ロードすることが望ましい.
    7.constとクラス静的メンバー
    同様に上のクラスを例にとります
    class A {
        private: int nun;
        public: int const_func()  {return 0;}	
    };
    A a;
    const A ca;
    a.const_func(); // success
    ca.const_func(); // error
    

    クラスで定義静的変数はクラスで初期化できず、クラス外で初期化しなければならない.上記はクラス外でint A::num = 0;に変更すべきである.
    しかし、例外があります.
    class A {
        private: static int nun = 0; // error
        public: int const_func() const {++num; return 0;}	// success.        
    };
    
    constは、作成時に初期化する必要があることを要求するため、上記の例が成立する.
    8.constとtypedef
    ポインタを毎回定義したくない場合は、typedefでポインタタイプを定義したいと考えています.次のようになります.
    class A {
        private: const static int nun = 0; // success
    };
    

    同じ操作を行う
    typedef char * Str;
    char *str1 = "hello";
    const char *str2 = "hello";
    const Str str3 = "hello"; //  gcc, g++     ,  vs17   ,         ,     ,    vs    cl       
    

    以上のようにすると、typedefは単なる置換ではなく、str 2と同じではなくconst Str str3に変換することが分かる.
    なぜなら、char *const str3が声明を書き換えると、実際のデータ型はchar *ではなくcharになり、逆にchar *が声明子の一部になり、*のデータ型はconst Strになり、const char*を修飾すると、定数ポインタになるからである.
    上記のtypedefで定義別名は、const char、例えばusing Type =で置き換えることができる.またtypedefの代わりにusingを使用することを提案する、typedefの機能usingはすべてあり、またtypedefはテンプレート化機能をサポートしていない.その後、using Type = char *の使用を推奨する理由を分析する.
    まとめ
    この節では、usingの用法に関する注意点の一部をまとめると、めまいがするように見えるかもしれないし、一度に覚えやすいわけではないので、見ているときにも検証したほうがいいと思います.最も主要に底層constと最上階constを覚えて、どのように重荷して、基本的に多くの問題はすべて派生します.