三法則

三法則（Rule of Three）以及五法則在C++裡，它是一個以設計的基本原則而制定的定律。

## 規則

它的要求是，假如類有明顯定義下列其中一個成員函式，那麼程式設計師必須寫入其他兩個成員函式到類別內，也就是說下列三個成員函式缺一不可。

- 解構子
- 複製建構子
- 設定運算子 (C++)

上述三個函式是特別的成員函式，假如程式設計師沒有自行定義或宣告這三個函式，編譯器會自動地建立他們並且編譯到應用程式內。然而，如果程式設計師僅定義其中一個，其餘兩個函式仍然會由編譯器自動產生，這種混雜的情況非常容易產生程式設計師難以預期的錯誤。三法則的存在，正是提醒程式設計師避免那樣的陷阱。

三法則這個專有名詞是由馬歇爾·克來恩於1991年創立。

它的修正版本是，假如類別有用到RAII，可以不必定義解構子，也就是所謂的二大定律。

因為隱性產生的建構子與設定運算子可以很容易地複製類別內所有的資料成員，當資料成員是指標型態時，指標位址會隨著類別而跟著被複製。要注意的是，直接地複製指標位址是一項非常危險的動作，所以只要類別有封裝指標型態的資料結構，或是類別有封裝外部參照的資料成員，例如指標型態的資料成員，程式設計師應該為此而定義顯性的複製建構子與設定運算子。

## 五法則

C++11新增兩個法則，稱為五法則：

- 解構子
- 複製建構子
- 設定運算子
- 移動建構子
- 移動賦值運算符

## 範例

頭文件 header.h｜主函式 main.cpp

```cpp
#ifndef _HEADER_H_
#define _HEADER_H_
//
// 判斷是否為微軟編譯器
#ifndef _MSC_VER
#undef NULL
#define NULL 0
#endif
//
#include <iostream>
#include <limits>
//
using std::cin;
using std::cout;
using std::endl;
//
// 類別：方塊
class CCube
{
public:
	// 建構子
	CCube();
	// 含有參數的建構子
	CCube(double length, double width, double height);
	// 三法則：解構子
	~CCube();
	// 三法則：複製建構子
	CCube(const CCube &sample);
	// 三法則：設定運算子
	CCube& operator=(const CCube &sample);
	// 設定長寬高
 	void setLength(double length);
 	void setWidth(double width);
 	void setHeight(double height);
	// 取得長寬高
	double getLength() const;
	double getWidth() const;
	double getHeight() const;
	// 計算體積
	double getVolume() const;
protected:
private:
	// 長寬高
	double m_Length;
	double m_Width;
	double m_Height;
};
//
void PAUSE(void);
//
#endif
```

```cpp
#include"header.h"
//
// 判斷是否為微軟編譯器
#ifndef _MSC_VER
int
#else
void
#endif
main(int argc, char* argv[])
{
	// 方塊零
	CCube cube0(4.3, 5.2, 6.1);
	// 第一個方塊
	{
		cout << "=== No.1 cube ===" << endl;
		CCube cube1 = cube0;
		cout << "Volume of cube = " << cube1.getVolume() << endl;
	}
	// 第二個方塊
	{
		cout << "=== No.2 cube ===" << endl;
		CCube cube2;
		cube2 = cube0;
		cout << "Volume of cube = " << cube2.getVolume() << endl;
	}
	PAUSE();
	return
#ifndef _MSC_VER
		EXIT_SUCCESS
#endif
		;
}
```

頭文件實作 header.cpp

```cpp
#include "header.h"
//
void PAUSE(void)
{
	cin.clear();
	cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
	cout << "press any key to continue...";
	cin.get();
}
CCube::CCube()
{
	cout << "Constructor: CCube()" << endl;
	this->m_Length = 0.0;
	this->m_Width = 0.0;
	this->m_Height = 0.0;
}
CCube::CCube(double length, double width, double height)
{
	cout << "Constructor: CCube(length, width, height)" << endl;
	this->m_Length = length;
	this->m_Width = width;
	this->m_Height = height;
}
CCube::~CCube()
{
	cout << "Destructor: ~CCube()" << endl;
	this->m_Length = 0.0;
	this->m_Width = 0.0;
	this->m_Height = 0.0;
}
CCube::CCube(const CCube &sample)
{
	cout << "Copy constructor: CCube(const CCube &sample)" << endl;
	//
	// 保護：禁止設值給自己
	if (this != &sample)
	{
		this->m_Length = sample.m_Length;
		this->m_Width = sample.m_Width;
		this->m_Height = sample.m_Height;
	}
}
CCube& CCube::operator=(const CCube &sample)
{
	cout << "Assignment operator: operator=(const CCube &sample)" << endl;
	//
	// 保護：禁止設值給自己
	if (this != &sample)
	{
		this->m_Length = sample.m_Length;
		this->m_Width = sample.m_Width;
		this->m_Height = sample.m_Height;
	}
	return *this;
}
double CCube::getVolume() const
{
	return (this->m_Length * this->m_Width * this->m_Height);
}
```