クラス-構築、プロファイル、コピー、モバイル使用の概要

10794 ワード

1.クラスのコピー制御メンバーを定義する方法
各関数の通常の宣言は、defaultキーワードによってコンパイラの自動合成が明示的に要求されます.
class Foo {
public:
	//  default         
	Foo() = default;					   //    

	Foo(const Foo &) = default;		       //    ,   const  
	Foo(Foo &&)      = default;			   //    ,         	

	Foo& operator=(const Foo &) = default;  //    ,   const  ,        
	Foo& operator=(Foo &&) = default; 		//    ,         ,         

	~Foo()=default;
};

3/5の法則:コンストラクション関数を除いて、他の5つのコピー制御メンバーは全体と見なすべきです.一般的に、クラスがいずれかの操作を定義している場合は、すべての5つの操作を定義する必要があります.たとえば、一部のクラスでは、コピー構造を定義し、コピーに値を割り当て、プロファイルを正しく動作させる必要がある場合、これらのクラスは通常、リソースを管理するために使用され、リソースをコピーすると追加のオーバーヘッドが発生します.このようなコピーが必要でない場合、このようなオーバーヘッドを回避するために、移動構造関数と移動付与演算子が定義される.
2.コンストラクション関数①すべてのパラメータがデフォルトのパラメータを提供する場合、実際にはデフォルトのコンストラクション関数も定義されます.
Sales_data(const std::string & s = ""): bookNo(s) { }
②クラスメンバーにconstがあり、デフォルトのコンストラクション関数を参照または提供していないクラスは、初期値リストからメンバーに初期値を提供する必要があります.注:初期値リストはメンバーの初期値のみを示します.初期化順序は定義できません.その順序はクラス定義と一致します.
class ConRef{
	public:
		ConRef(int li);
	private:
		int i;
		const int ci;
		int& ri;
};

//  :ci ri   
ConRef::ConRef(int li)
{ //  
	i  =  li;	//  
	ci = li;    //  :   const  
	ri = i;     //  :ri       
}

//  :
ConRef::ConRef(int &li):i(li),ci(li),ri(li) {}
③C++11委託コンストラクタでコンストラクタを定義できます
Sales_data(const std::string &book, unsigned cnt, double price);
Sales_data():Sales_data("",0,0) {}; //   
④抽象ベースクラスを定義する場合、ユーザにインスタンス化されたくない場合は、構築とプロファイルの役割ドメインをprotectedと定義することができる
C++11では抽象ベースクラスの方法で=0と定義してもよい.
class People
{
	void work() = 0;   //  =0,   
protected:
	People();          //  derived    
	virtual ~People();
}

People people; //   ,     
⑤initializer_を使用可能listクラス内のコンテナメンバーの初期化
//#include 	        
class strvec{
public:
	strvec(initializer_list li):vec(li){}	//copy(li.begin(),li.end(),back_inserter(vec));
private:
	vector vec;
};

//    
strvec vec = {"hello", "goodbye"};  //    ,vec     string:hello goodbye
⑥派生クラスを初期化する場合、そのベースクラス構築関数が先に呼び出され、構築関数の非staticメンバーがクラスの定義順に初期化される
class student: public people; //people       
⑦virtual関数を構築または構築中に呼び出さないでください.そうしないと、現在の構築ベースクラスのバージョンが呼び出されます.簡単に言えばbase class構造の間virtual関数はvirtual関数ではありません
class Log{
public:
	Log() { open();}	//      virtual open
	virtual bool open() const = 0;
};

class TxtLog:public Log
public:
	virtual bool open() const;
};

//      txt  ,          base class Log::Log()
//         base class open,     
TxtLog log; 
⑧オブジェクトメモリの割り当てと初期化は、allocatorクラスによって分離できます.一般的にnewによるメモリ割り当てやオブジェクト構造は不要ですが、allocatorクラスでは問題ありません.
const *const p = new string[n]; 	//  n  string 

//  allocator
allocator alloc;			//    string allocator  
auto const p = alloc.allocate(n);	//  n      string
auto q = p; 						//q             
alloc.construct(q++);				//*q     
alloc.construct(q++, "hello");		//*q hello
⑨explicitキーワードで暗黙的な変換を回避できます.この場合、直接初期化することでしか使用できません.
//   explicit,        
struct Sales_data{
	Sales_data(const string& s): bookNo(s) { };
	Sales_data combine(const Sales_data&)
};

string book("string"); //
item.combine(book);	   //  ,         Sales_data
item.combine("string"); //  ,         

//  explicit,        
struct Sales_data{
	//       explicit
	explicit Sales_data(const string& s): bookNo(s) { }; 
	Sales_data combine(const Sales_data&)
};
string book("string"); //
item.combine(book);	   //  

//             
Sales_data item1(book);    //  
Sales_data item2 = book;   //  

3.コピーコンストラクタ①定義:最初のパラメータは自己タイプの参照(通常は定数参照)であり、その他のすべてのパラメータ(あれば)にデフォルト値がある
Foo::Foo(Foo rhs);  		//  ,         ,          
Foo::Foo(const Foo& rhs); //  
②はパラメータが特殊な構造関数であるため、初期値リストを使用して関数を初期化することができるデータメンバー
Sales_data(const Sales_data& s): bookNo(s.bookNo) { }
③IOクラス、unique_ptrのクラスはコピーが許可されていない場合、キーワードdeleteで削除できます.
注:privateとして宣言することもできますが、コピーを阻止するために定義しません(友元関数とメンバー関数のコピーを防止します).
Sales_data(const Sales_data& s) = delete;
④オブジェクトをコピーするときは、その各成分を忘れないでください.そのため、必要でなければ、システムが自動的に合成したcopy構造関数を採用することが望ましいです.
//  1:           
class people{
public:
	people(const people& s):name(s.name) {}
private:
	string name; 
}; 

//      age ,                
class people{
public:
	people(const people& s):
	name(s.name){} 	//   age     
private:
	string name;
	int    age; 
}; 

//  2:                  
//   ,             ,      ,s          
struct student:public people{
	student(const student & s):id(s.id){}
	student& operator=(const student& s) {id = s.id;} 
private:
	int	id;		//   
}; 

//  :
struct student:public people{
	student(const student & rhs): 
		people(rhs), id(rhs.id){} //    copy   
	student& operator=(const student& rhs) 
	{
		people::operator=(rhs);		//    copy assignment 
		id = rhs.id;
		return *this;
	} 
}

4.コピー付与演算子copy構造の③④の注意事項に加え、以下の注意事項がある
①代入演算は、通常、左側の演算対象への参照を返す
//           ,   student      copy assignment       
student& operator=(const student& s); 
②賦値操作の場合、自賦値を正しく処理できるものとし、
注意:一部の場合、swap+copy構造で自己付与を避けることができ、後続の移動構造で減少する
class HasPtr{
public:
	HasPtr& operator=(const HasPtr &p);
private:
	string *ps;
};

inline HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
	auto newp = new string(*rhs.ps);  //     ,  rhs this      
	delete ps;       				  
	ps = newp;       					
	return *this;
}
③メモリリソース管理を含むクラスで、copy assignment関数は一般的にコピー構造とプロファイルの2つのプロセスを含みます.
重複コードを回避するには、メンバーコピー構造とプロファイルをそれぞれ2つのプライベート関数として作成し、copy構造、copy付与、プロファイル使用に使用できます.
5.構造関数の使用
一部の注意事項はconstructorコンストラクション関数を参照してください
①構造解析関数では、まず関数体を実行し、次にメンバーを破棄し、メンバーを初期化順の逆順序で破棄します.drive classの場合
driveプロファイルを呼び出してからbass classプロファイルを呼び出します.プロファイル関数はメンバーを直接破棄しないことに注意してください.
class people{
public:
  ~people() {} // ( /  )          ,        ,  int -> string 
private:
   string name;
   int    i;
}
②構造関数を呼び出す場合
//1)             ;
//2)         ,      
//3)         ,      
//4)         ,  delete      
//5)      ,             ,   

{//     
	string *p = new string();		 //p       
	auto p2 = make_shared(); //p2   shared_ptr(      )
	string item(*p);				 //       *p   item 
	vector vec;				 //    
	vec.push_back(*p2);		         //  p2     
	delete p;						 // p           
}
//       ; item, p2 vec      
//  p2        ;        0,     
//  vec       
③オブジェクトへの参照またはポインタが役割ドメインから離れた場合、構造関数は実行されません.
{//     
	string *p = new string();		 //p       
}
//       delete p,        
④クラスにmove構造と付与演算子が含まれている場合、構造関数が正しく実行されることを保証しなければならない.
class HasPtr{
   HasPtr(string s = ""):ps(new string(s)){}
   HasPtr(HasPtr&& rhs):ps(rhs.ps) (rhs.ps = nullptr;)
  ~HasPtr() { if(ps) delete ps;}
  private:
	string *ps;
}

{//     
HasPtr ptr1("hello));
HasPtr ptr2(std::move(ptr1));  
}
//ptr1    ,      ,    if(ps)       
⑤構造関数は絶対に異常を吐かないでください.解析関数によって呼び出された関数が異常を放出する可能性がある場合、解析関数
異常をキャプチャし、プログラムを吐くか終了する必要があります.詳細はeffective c++条項08を参照
⑥マルチステートベースクラスに対してvirtual構造関数を宣言する
class people{
  public:
  ~people() {cout<< "~people()" << endl;}
};

class student:public people {
  public:
  ~student() {cout<< "~student()" << endl;};
};
  
//    
people *p = new student();
delete p;  //  ,   people   

//    
~people() 

6.移動構造関数の本質は特殊な構造関数であり、関連する注意事項は構造とコピー構造関数を参照する.
①移動は所与のオブジェクトから「コピーリソースではなくリソースを盗み取るため、移動操作に異常は発生しないことが一般的である.
定義時にnoexceptキーワードを追加できます.
class StrVec{
public:
	StrVec(const StrVec&);
	StrVec(StrVec&&) noexcept;
	StrVec& operator=(StrVec&&) noexcept;
	
	~StrVec() noexcept;
};
②移動後のオブジェクトを破棄したり、新しい値を付与したりすることができますが、使用できません.
 string str1("world");
   string str2(std::move(str1));
   cout<< str1[0] << endl;			//  ,str1     ,     
   str1 = string("hello");  		//  ,       
③移動操作を行う場合、移動された関数が正確に解析されることを保証できる
class HasPtr{
public:						//      ,  rhs    
   HasPtr(HasPtr&& rhs):ps(rhs.ps) {rhs.ps = nullptr;} 
  ~HasPtr() { if(ps) delete ps;}
private:
	string *ps;
}
④クラスに使用可能なコピーコンストラクタがあり、コンストラクタを移動しない場合、そのオブジェクトはコピーされます.
コンストラクション関数は「移動」し、コピー割り当ても移動割り当てと同じです.
class Foo {
public:
	Foo(const Foo &) = default;		       //    ,   const  
	Foo& operator=(const Foo &) = default;  //    ,   const  ,        

	~Foo()=default;
};

Foo x;
Foo y(x);				//      ;x     
Foo z(std::move(x));	//      ,           

7.移動割付演算子移動構造関数に注意すべき事項に加えて、移動割付演算子は以下の状況に注意する必要がある
①関数は、関数効率を向上させるために右の参照パラメータを提供することができる
//             :      ,      
void push_back(const X&);	//  :        X
void push_back(X&&);		//  :             

vector vec;
string s = "void push_back(const X&);";
vec.push_back(s); 			//  
vec.push_back("done");		//  : done         
②移動賦値演算を行う場合は、自己賦値の場合を考慮する.
class HasPtr{
	friend void swap(HasPtr& lhs, HasPtr& rhs); 
public: 
	HasPtr(const string &s = string(), int i = 0);
	//HasPtr& operator=(HasPtr p);  //
	HasPtr& operator=(const HasPtr &p);
	HasPtr& operator=(HasPtr &&p);
	
private:
	string *ps;
	int		ival; 
};

inline
void swap(HasPtr& lhs, HasPtr& rhs){
	cout<< "void swap(HasPtr& lhs, HasPtr& rhs)"<
③一時オブジェクト付与と変数付与と移動の違いに注意:
//   
HasPtr ptr1, ptr2, ptr3;
ptr1 = ptr2;   		   //    copy assignment
ptr1 = string("hello"); //       constructor(    )->move assignment->destructor(    )
ptr1 = std::move(ptr3); //    
④代入演算子と移動操作をコピーして交換することができます.この操作の鍵は一時変数を作成することです.
割り当てオブジェクトと対話して、自己割り当てを回避しますが、一時変数の作成が必要なため、不要なオーバーヘッドが発生します.
HasPtr& operator=(HasPtr p);  //                 
//HasPtr& operator=(const HasPtr &p);
//HasPtr& operator=(HasPtr &&p);

HasPtr ptr1, ptr2, ptr3;
ptr1 = ptr2;   		   //  ,       copy constructor -> assignment
ptr1 = string("hello"); //       constructor(    )->assignment->destructor(    )
ptr1 = std::move(ptr3); //  ,       move constructor -> assignment