C++ベース-いくつかの詳細、よくミスを犯す要約

21560 ワード

  • 詳細1 numeric_limits doublemin
  • 詳細2型変換演算子関数リロードの呼び出しタイミング
  • 詳細3 const char s hello world
  • 詳細5 dequevectorstackqueue
  • 詳細6 enumタイプ要素の値
  • 詳細7 cout 1 string
  • を出力できない理由
  • 詳細8 this式は、変更可能な左の値
  • でなければなりません.
  • 詳細9 collbeginとcollfront
  • 詳細10 strlength strsize
  • 詳細11 push_backconst value_type
  • 詳細12クラスにおける非純虚の関数は、
  • を実装する必要がある.
  • 詳細13虚関数はimplementationを与えなければならない.そうしないと
  • をインスタンス化できない.
  • 詳細14 implementationの位置
  • 詳細15同じファイルに同じ名前のクラステンプレートがない
  • 詳細16 int i getIntとint i getInt
  • 詳細17プライベートメンバーのアクセスタイミング
  • 詳細18スタックメモリ付きクラス
  • 詳細19ポインタの所有権
  • を簡単に変換
  • Reference

  • 詳細1:numeric_limits::min()
    各内蔵データ型の精度、極値があるヘッダファイルは.
    numeric_limits::min()は,−∞ではなく無限小量(0に近い)を表す.
    cout << numeric_limits<double>::min() << endl;
    2.22507e-308
    cout <double>::max() << endl;
    -1.79769e+308       //            

    ここで注意しなければならないのは、
    numeric_limits<double>::min() != 
    numeric_limits<double>::epsilon()
    cout <double>::epsilon() << endl;
    2.22045e-016

    msdnのepsilon()関数に対する戻り値の説明:
    The function returns the difference between 1 and the smallest value greater than 1 that is representable for the data type.
    すなわち、numeric_limits::epsilon()の戻り値は、コンピュータが2つの同型のデータが等しいか否かを判断できる限界であり、すなわち、2つの数の差がこのepsilonよりも小さい場合、コンピュータから見れば2つの数が等しい.
    double x = 1.;
    double y = 1.+pow(10, -17);
    cout << (x == y) << endl;   // 1

    詳細2:タイプ変換演算子関数のリロードの呼び出しタイミング
    タイプ変換演算子関数のリロードは、関数の戻り値がないため、完全なメンバー関数ではありません. の内部にはreturn式がありますが.
    タイプ変換演算子関数のリロードは、表示される呼び出しタイプ変換演算子である場合があり、非常に隠れた場所で発生する場合があります.
    class Fraction
    {
    public:
        Fraction(int _numerator, int _denominator):
                numerator_(_numerator), denominator_(_denominator)
        {}
        operator float() const 
        {
            return static_cast<float>(numerator_)/denominator_;
        }
    private:
        int numerator_, denominator_; 
    }
    
    int main(int, char**)
    {
        Fraction frac(3, 5) ;
        float f1 = float(frac);       //            
        float f2 = frac;              //               
        return 0;
    }

    クラスの初期化パラメータのリストで、より隠れた状況が発生します.
    class Floater
    {
    public:
        Floater():val_(Fraction(1, 1)){}
    private:
        float val_;
    }
    
    int main(int, char**)
    {
        Floater f;      
            //   Floater                       
        return 0;
    }
    

    詳細3:const char*s=「hello world!」
    const char* s = "hello";
    sは自然にconst char*タイプであり、"hello"のタイプはconst char[6](パケット拡張文字列定数の末尾の\0)が配列タイプであり、sはそのヘッダアドレスであり、const char*を受け入れる関数は自然に"hello"のような文字列タイプを受け入れることができない.
    詳細5:deque、vector、stack、queue
    ようき
    挿入
    削除
    表示
    deque
    push_front(ヘッドプラグ)push_back(テールプラグ)
    pop_back pop_front
    back front
    vector
    push_back
    pop_back
    back front
    stack
    push
    pop
    top
    queue
    push
    pop
    backfront
    dequeは両端の挿入、削除、表示を行う両端キューとも呼ばれます.stackのすべての操作は、一端、すなわち末端でのみ行うことができる.
    詳細6:enumタイプ要素の値
    enum Color {red, yellow, blue, white, black, numColors};

    デフォルト(最初の要素に初期値を付与しないことを前提として)、red == 0、次いで順次増加します.この場合、numColorsの値は5、つまりColorという列挙タイプに含まれる要素の個数は5です.任意の要素に任意の整数値を任意の位置で付与することができ、その後の要素は順次1増加します.
    詳細7:cout stringを出力できないのはなぜですか?
    #include
    int main(int, char**)
    {
        std::string str("hello");    //   
        std::cout << str << std::endl;
        //   ,        (operand,std::string)    "<
        return 0;
    }
    coutstringタイプを出力できないなんて驚きですか?そのため、STLの多くのヘッダファイル(Visual C++環境を含む)は、std::basic_stringクラスの定義式を含み、クラスを間接的に含んでいる(ただし、), includeのヘッダファイル( の#include のように)を んではいけないため、std::stringクラスを することができる.
    typedef basic_string<char, char_traits<char>, allocator<char> >
        string;         
        // string                    

    しかし、 は、それに するoperator<<ヘッダファイルに されていることであり、 で まなければならないことです.したがって、(すなわち、operator<<を む)を むだけで、cout std::stringの を することができます.
    #include 
    #include 
    int main(int, char**)
    {
        std::string str("hello");
        std::cout << str << std::endl;
        return 0;
    }

    の はVisual C++ にのみ であり、つまりほとんどのSTLのヘッダファイルにはstd::basic_stringの が まれており、これらのヘッダファイルを むだけでstd::stringクラスを することができ、operator<<を するにはヘッダファイルを で む がある.これらの および は、Visual C++ にのみ であることを します.
    8:*this: は な の でなければなりません
    *thisは な ではありません
    class Widget
    {
    public:
        void foo()
        {
            cout << typeid(this).name() << endl;               
                // this    const Widget* const
        }
    }
    thisは クラスオブジェクトのアドレスを し、クラスオブジェクトの が すると、メモリにメモリが り てられます.このメモリのアドレスはthisの で、メモリ のオブジェクトの は に されません.
    9:coll.begin()とcoll.front()
    vector<int> coll;

    #カンスウ#
    り のタイプ
    coll.begin()
    vector::iteratorvector::const_iterator
    coll.front()
    vector::referencevector::const_reference
    coll.end()
    vector::iteratorvector::const_iterator
    coll.back()
    vector::referencevector<int>::const_reference vectorの を できます.
    template<typename T, class Alloc = alloc>
    class vector
    {
    public:
        typedef T           value_type;
        typedef value_type* pointer;
        typedef value_type* iterator;
        typedef value_type& reference;
        typedef size_t      size_type;
        typedef ptrdiff_t   difference_type;
    }

    10:str.length()str.size()
    は の いもありません.ソースコードの に、 はありません.
    size_type length() const _NOEXCEPT
        {   // return length of sequence
        return (this->_Mysize);
        }
    
    size_type size() const _NOEXCEPT
        {   // return length of sequence
        return (this->_Mysize);
        }

    11:push_back(const value_type&)
    [C++ ライブラリ]のこの を します.すべての はvalue ではなくreference を しています.
    void push_back(const value_type& _Val);
    push_back が け れるパラメータタイプはreferenceタイプであり、 と なさないでください. の を することを します.push_back は、 された を にコピーすることがわかります.vectorコンテナのpush_backメンバー だけでなく、 のどの を り ってみると、コンテナが に された のためにコンテナ のコピーを しているかは、すべてのコンテナがvalueの を していることを しています.
    12:クラス の な の は されなければならない
    class Base
    {
    public:
        //     DerivedA  fooA,DerivedB  B
        //        fooA fooB        ,
        //              fooA fooB
        //         DerivedB    fooA  
        //  DerivedA    fooB  ,                
        virtual void fooA();
        virtual void fooB();
        virtual void foo() = 0;
    }
    inline void Base::fooA()
    {
        throw exception("cannot be called");
    }
    inline void Base::fooB()
    {
        throw exception("cannot be called");
    }
    
    class DerivedA :public Base
    {
    public:
        void foo(){}    //         ,         
        void fooA() { cout << "DerivedA::fooA()" << endl;}
    }
    
    class DerivedB :public Base
    {
    public:
        void foo(){}
        void fooB()
        {
            cout << "DerivedB::fooB()" << endl;
        }
    }
    
    int main(int, char**)
    {
        DerivedA da;
        da.fooA();   // "DerivedA::fooA()"
        da.fooB();   //    
        return 0;
    }

    13: はimplementationを えなければなりません.そうしないとインスタンス できません.
    この の なセリフは、 が を える がなく、クラスをインスタンス することもできます.
    class A
    {
    public:
        void foo1();
        virtual void foo2();
    }
    
    int main(int, char**)
    {
        A a;        // foo2          
        return 0;
    }
    class A
    {
    public:
        void foo1();
        virtual void foo2() {}
    }
    
    int main(int, char**)
    {
        A a;          //   ,   A        
        a.foo2();     //   ,     foo2   
        a.foo1();     //   ,      foo1   
        return 0;
    }

    の な : を します.システムには の オーバーヘッド(メモリ り て)が であり、1つのクラスに がある 、コンパイルシステムはそのクラスのために テーブル(virtual function table、 vtable)を する.これはポインタ であり、 のエントリアドレスを し、 を う の オーバーヘッドは ないため、マルチステートは である.
    14:implementationの
    // A.hpp
    class B;
    class A
    {...};
    
    // A.cpp
    A's somefuncs' implementation goes here
    
    // B.hpp
    class B
    { ... };
    
    // B.cpp
    B's implementation
    A's implementation 
                // A           B     
                //  A hpp   ,   B       

    15: じファイルに じ のクラステンプレートは できません
    の 、 ( テンプレートにおける の なるパラメータ( と を む)とは なり、 も な いは にタイプ メカニズムがないことである. の で れるには、 の バージョンとしてしか れない.
    たとえば、 クラス(テンプレートパラメータリストで のタイプがバイトを める)について します.
    template<typename F, tyepname... FS>
    struct variant_helper
    {
        static const size_t size = sizeof(F) > variant_helper::size ? sizeof(F):variant_helper::size;
    };
    
    //   ,      ,      
    template<typename T>
    struct variant_helper
    {
        static const size_t size = sizeof(T);
    }
    
    //   ,          
    template<typename T>
    struct variant_helper
    {
        static const size_t size = sizeof(T);
    }

    たような を てみましょうが、 のリロード が されています.
    //            `...`        ,       
    //              
    template<typename T, typename... Types>
    void print(const T& firstArg, const Types&... types)
    {
        cout << firstArg << endl;
        print(types...);
    }
    
    //   ,    
    template<typename T>
    void print(const T& arg)
    {
        cout << arg << endl;
    }

    16:int i=getInt()int&&i=getInt();
    int i = getInt();   // i:   ,getInt()         
    int&& i = getInt(); // getInt()           ,          ,   

    17:プライベートメンバーのアクセスタイミング
    オブジェクトではなくクラスの では、パラメータとして された のオブジェクトであっても、 のプライベートメンバーにアクセスできます.
    class A
    {
    public:
        A():m_ptr(new int(0)){}
        A(const A& a):m_ptr(new int(*a.m_ptr)){}
                    //     a     
    private:
        int* m_ptr;
    }

    18:スタックメモリ きクラス
    メンバー としてスタックメモリを つクラスでは、デフォルトのコピーコンストラクタが いコピーであり、ポインタサスペンション(dangling pointer)の が する いコピーコンストラクタを する があります.
    class A
    {
    public:
        A():m_ptr(new int(0)) {}
        A(const A& a):m_ptr(new int(*a.m_ptr)){}
                //          
        ~A() {delete m_ptr;}
    private:
        int* m_ptr;
    }
    A getA()
    {
        return A();
    }
    
    int main(int, char**)
    {
        A a = getA();
        return 0;
    }

    いコピーコンストラクション が されていない 、 のコードはコンパイルエラーが し、 のm_ptrは2 され、geta() の で1 し、 に コンストラクションのときに1 され、2 のmain のローカルオブジェクトaコンストラクションのときに1 されます. いコピーであるため、この2つのオブジェクトのm_ptrは じポインタであり,これがいわゆるポインタサスペンションの である. いコピー が されない 、A b(a);は、bのm_を する.ptrポインタもaオブジェクトのm_を して した.ptr.
    19:ポインタの を に
    int* p1 = new int(10);
    int* p2 = p1;   
    delete p1;
    cout << *p2 << endl;    //            

    ポインタの :
    int* p1 = new int(10);
    int* p2 = p1;
    p1 = nullptr;
    cout << *p2 << endl;    //    

    Reference
    [1] numeric_limits::epsilon [2] Why cannot cout a string