C++ベース-いくつかの詳細、よくミスを犯す要約
21560 ワード
詳細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;
}
cout
がstring
タイプを出力できないなんて驚きですか?そのため、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