##『More Effective C+』-基礎議題

4273 ワード

More Effective C++
#@author:       gr
#@date:         2015-05-11
#@email:        [email protected]

一、pointersとreferencesをよく区別する
1.1. 初期化
ポインタは初期化せずに、参照は初期化する必要があります.
参照にnull referenceがない、ポインタをNULLにすることができる.
//      0,NULL,nullptr
char *str = 0;
int a = 1;
//       ,int &b;    
int &b = a;

1.2. こうりつ
null referenceは存在しないため、参照使用前にその有効性をテストする必要はなく、ポインタよりも効率が高い.
void print(const double& rd)
{
	cout << rd;		//        
}
void print(const double* rd)
{
	if (*rd)
		cout << rd;		//        
}

1.3. しこう
ポインタは、指すオブジェクトを変更し、referenceは常に最終的に指すオブジェクトを指します.
string s1("aaa");
string s2("bbb");
string& rs = s1;
string *ps = &s2;
rs = s2;			//  ,   s1, s1    
ps = &s1;			//  ,    s1

異なる時間に異なるオブジェクトを指し、ポインタを使用します.オブジェクトを表すと変更できません.referenceを使用します.たとえばoperator[]オペレータは、リファレンスを使用します.戻りポインタが値を割り当てるときに次のように書く必要がある場合は、直感的ではなく、誤解を生みやすい.
vector<int> a(10);
a[5] = 2;		//    
*a[5] = 2;		//    

二、C++変換オペレータを使用することが望ましい
2.1. 新しいモデルチェンジ
static_cast<type>(expression)		//      
const_cast<type>(expression)		//      
dynamic_cast<type>(expression)		//    ,       ,    ,  null   
reinterpret_cast<type>(expression)	//       ,     

2.2. reinterpret_cast
typedef void (*FuncPtr)();
FuncPtr funcPtrArray[10];
int doSomething();			// doSomething funcPtrArray 
funcPtrArray[0] = &doSomething;		//  ,    ,    
funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething);

2.3. 新しいものの代わりに古いものを使うと、アップグレードが容易になります.
#define static_cast(TYPE, EXPRESSION) ((TYPE) (EXPRESSION))
#define const_cast(TYPE, EXPRESSION) ((TYPE) (EXPRESSION))

新式の転換は長くて臭いが、新式の転換を使うのはもっとはっきりしていて、できるだけ転換の使用を減らすことができます.
三、配列を絶対に多態(Polymorphism)で処理しない
3.1. マルチステート配列に存在する問題
class BST{...};
class BalancedBST : public BST{...};

void printBSTArray(ostream& s, const BST array[], int numElements)
{
	for (int i = 0; i < numElements; ++i)
	{
		s << array[i];
	}
}

BST BSTArray[10];
printBSTArray(cout, BSTArray, 10);		//    

BalancedBST bBSTArray[10];
printBSTArray(cout, bBSTArray, 10);		//    ,    

サブクラスの配列を多態化しようとすると,サブクラスの大きさが親クラスの大きさに解析されるため,所望の結果が得られないことが多い.これにより予測不可能な結果が生じる.
同様に配列削除を行う場合も同様に問題が発生するため,配列とマルチステートは一緒に使用しない.
このエラーは、「特定のクラスは別の特定のクラスから継承しないでください」という条項33に記載されていることを忘れないでください.
四、必要でなければdefault constructorsを提供しない
4.1. 無中生有
一部のクラスは「無中生有」で生成できるが、一部のクラスは「無中生有」ではない.このような生成の対象は意味がないからだ.例えば、1つの通信帳フィールドのclassは、外部から指定された人名が得られなければ、生成されたオブジェクトは意味がありません.これらのオブジェクトは、default constructorを提供するべきではありません.
4.2. default constructorの欠如による問題
次のEquipmentPieceのように、デフォルトの構造関数はなく、いくつかの問題が発生します.
class EquipmentPiece{
public:
	EquipmentPiece(int IDNumber);			//       ctors,          defalut ctors
};
  • 配列を定義するとき、タイプ配列
    EquipmentPiece bestPiece[10];			//  ,    EquipmentPiece ctors
    EquipmentPiece *bestPieces = new EquipmentPiece[10];		//  
    を生成できません.この問題を解決するには、オブジェクト配列ではなくポインタ配列を使用します.
    typedef EquipmentPiece* PEP;
    PEP bestPiece[10];						//  ,  10        
    PEP *bestPieces = new PEP[10];		//   
    for (int i = 0; i < 10; ++i)
    	bestPieces[i] = new EquipmentPiece( ID Number );
  • それらは多くのtemplate-based container classesには適用されず、これらのcontrainerはインスタンス化の目標がdefault constructorsであることを望んでいる.
    template <typename T>
    class Array{
    public:
    	Array (int size);
    private:
    	T *data;
    };
    template <typename T>
    Array<T>::Array(int size)
    {
    	data = new T[size];			//   ,    default ctors
    }
    の解決策は、templateを慎重に設計し、default ctorsに対する需要を解消することである.たとえば、vectorはデフォルトのコンストラクション関数を必要としません.
  • virtual base classesの場合、すべての派生クラスがvirtual base classconstructors引数を提供する必要があるという問題があります.

  • 4.3. デフォルトのコンストラクション関数に存在する問題の提供
  • 他のメンバーは、IDが存在するかどうかを確認し、他のmember functionを複雑にする必要がある.
  • classの効率に影響する.