天天看點

Visual C++ 2010 第7章 自定義資料類型

7.1 C++中的結構

  • 結構能夠實作 使用一種資料類型來包含所有需要的資訊。
  • 結構是使用關鍵字struct 定義的使用者定義類型。

    7.1.2 定義結構

struct BOOK // 建立一種新的變量類型,名稱是BOOK
{
	char Title[80];
	char Author[80];
	char Publisher[80];
	int Year;  // { }内的元素稱作BOOK結構的成員或字段,BOOK類型的每個對象都包含{ }内的成員
};
// struct 關鍵字 将BOOK 定義成類型名稱;
// 構成本類型對象的元素是在大括号内定義的;
// 注意,結構内的元素可以是除所定義的結構類型以外的任何類型,即不能包含類型為BOOK的元素。 但可以包括BOOK類型變量的指針。

// 建立BOOK類型的變量:
BOOK Novel; //聲明了一個名為Novel的BOOK類型的變量
           

7.1.3 初始化結構

  • 在聲明語句中定義初始值:
BOOK Novel:
{
	"Paneless Programming",
	"I.C. Fingers",
	"Gutter Press",
	1981
};
           

7.1.4 通路結構的成員

使用成員選擇操作符“ . ”

Novel.Year = 1988 ; // 将Year成員的值設定為1988
// 可以完全像使用與成員類型相同的其他變量那樣使用該結構成員
Novel.Year += 2 ;
           
// 可能在庭院裡發現的對象
#include <iostream>
using std::cout;
using std::endl;

struct RECTANGLE
{
	int Left; 
	int Top;
	int Right;
	int Bottom;
};

long Area(const RECTANGLE& aRect);

void MoveRect(RECTANGLE& aRect, int x, int y);

int main()
{
	RECTANGLE Yard = {0,0,100,120};
	RECTANGLE Pool = {30,40,70,80};
	RECTANGLE Hut1,Hut2;

	Hut1.Left = 70;
	Hut1.Top = 10;
	Hut1.Right = Hut1.Left + 25;
	Hut1.Bottom = 30;

	Hut2 = Hut1;
	MoveRect(Hut2,10,90);

	cout << endl
		 << "Coordinates of Hut2 are "
		 << Hut2.Left << "," << Hut2.Top << " and "
		 << Hut2.Right << "," << Hut2.Bottom;

	cout << endl
		 << "The area of the yard is "
		 << Area(Yard);

	cout << endl
		 << "The area of the pool is "
		 << Area(Pool)
		 << endl;
	
	return 0;
}

long Area(const RECTANGLE& aRect)
{
	return (aRect.Right - aRect.Left)*(aRect.Bottom - aRect.Top);
}

void MoveRect(RECTANGLE& aRect, int x, int y)
{
	int length(aRect.Right - aRect.Left);
	int width(aRect.Bottom - aRect.Top);

	aRect.Left = x;
	aRect.Top = y;
	aRect.Right = x + length;
	aRect.Bottom = y + width;
	return;
}
           
Visual C++ 2010 第7章 自定義資料類型

7.1.6 RECT結構

windows.h頭檔案中有一個預定義的RECT結構:

struct RECT
{
	LONG left;
	LONG top;
	LONG right;
	LONG bottom;
}
// 類型LONG 是等價于基本類型long的Windows類型
// 該結構通常用來定義顯示器上用于各種目的的矩形區域
           

InflateRect()函數:使矩形尺寸增加

EqualRect()函數:比較2個矩形

7.1.7 使用指針處理結構

定義指向RECT對象的指針:

RECT* pRect(nullptr);
//使用取址運算符将aRect變量的位址賦予pRect指針:
pRect = &aRect;
           

struct 包含指向struct 的指針

struct ListElement
{
	RECT aRect;
	ListElement* pNext; // 指向ListElement類型結構的指針
};
           

該定義使 ListElement 類型的對象可以菊連接配接到一起,其中每個ListElement對象可以包含下一個ListElement對象的位址,最後一個對象包含的指針為nullptr。

Visual C++ 2010 第7章 自定義資料類型

圖中每個方框代表1個ListElement類型的對象,除最後一個對象的pNext是nullptr外,每個對象的pNext成員都存儲着鍊内下一個對象的位址,這種安排稱為連結清單。

  • 優點:隻要知道表中第一個元素,就能找到所有元素。

1.通過指針通路結構成員

RECT aRect = {0,0,100,100};
RECT* pRect (&aRect);
(*pRect).top += 10;
           

2.間接成員選擇符

間接成員選擇符 ->

pRect->Top += 10;
           

7.2 資料類型、對象、類 和執行個體

  • C++ 語言引入新關鍵字 class 來描述類這一概念。 其與struct基本相同。
class CBox  //定義CBOX類,定義新的資料類型 CBOX
{
	 public:
	 	double m_Length;  // 類的資料成員
	 	double m_Width;
	 	double m_Height;
}

CBox bigBox; // 聲明一個表示CBox類執行個體的變量bigBox
           
  • 類名都以C作為字首;
  • 資料成員添加m_ 字首;
  • public:以該關鍵字定義的類成員通常是可以通路的,類成員預設是私有的。

7.2.2 類的操作

在C++中,可以建立新的資料類型——類,來表示任何希望表示的對象。類,不僅僅限于容納資料,還可以定義成員函數,甚至可定義在類對象之間使用标準C++ 運算符執行的操作。

CBox box1;
CBox box2;
if(box1 > box2);
	box1.fill();
else 
	box2.fill();
           

7.2.3 術語

  • 類:是使用者定義的資料類型。
  • 面向對象程式設計(OOP): 是一種程式設計風格,它基于将自己的資料類型定義成類的思想,這些資料類型專用于打算解決的問題的問題域。
  • 聲明類的對象:稱作執行個體化,因為這是在建立類的執行個體。
  • 類的執行個體:稱為對象。
  • 對象 在定義中隐式的包含資料和操作資料的函數,這種思想稱為 封裝。
  • 對象的實際用途: 根據問題域所特有的對象編寫程式,圍繞C++類的所有功能都是為了使之盡可能的全面和靈活。

7.3 了解類

  • 類組合了構成對象的元素資料的定義和處理本類對象中資料的方法。
  • 類中的資料和函數稱為類的成員。資料的類成員稱為資料成員;函數的類成員稱為函數成員或成員函數。
  • 類的成員函數 有時也稱作方法。
  • 資料成員還稱作字段。

7.3.1 定義類

class CBox  //定義CBOX類,定義新的資料類型 CBOX
{
	 public:
	 	double m_Length;  // 類的資料成員
	 	double m_Width;
	 	double m_Height;
}
           

public 關鍵字決定着後面那些類成員的通路屬性。将資料成員指定為public ,意味着在包含這些成員的類對象的作用域内任何位置都可以通路它們。還可以将類成員指定為private 或 protected ,如果省略通路說明,預設是private。

7.3.2 聲明類的對象

CBox box1;
CBox box2;
           
Visual C++ 2010 第7章 自定義資料類型

7.3.3 通路類的資料成員

#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	double m_Length;
	double m_Width;
	double m_Height;
};

int main()
{
	CBox box1;
	CBox box2;

	double boxVolume(0.0);

	box1.m_Height = 18.0;
	box1.m_Length = 78.0;
	box1.m_Width = 24.0;

	box2.m_Height = box1.m_Height - 10;
	box2.m_Length = box1.m_Length/2.0;
	box2.m_Width = 0.25*box1.m_Length;

	boxVolume = box1.m_Height*box1.m_Length*box1.m_Width;

	cout << endl
		 << "Volume of box1 = " << boxVolume;

	cout << endl
		 << "box2 has sides which total "
		 << box2.m_Height + box2.m_Length + box2.m_Width 
		 << "inches.";

	cout << endl
		 << "A CBox object occupies "
		 << sizeof box1 << "bytes.";

	cout << endl;
	return 0;

}
           

7.3.4 類的成員函數

#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	double m_Length;
	double m_Width;
	double m_Height;

	double Volume()
	{
		return m_Length*m_Width*m_Height;
	}
};

int main()
{
	CBox box1;
	CBox box2;

	double boxVolume(0.0);

	box1.m_Height = 18.0;
	box1.m_Length = 78.0;
	box1.m_Width = 24.0;

	box2.m_Height = box1.m_Height - 10;
	box2.m_Length = box1.m_Length/2.0;
	box2.m_Width = 0.25*box1.m_Length;

	boxVolume = box1.Volume();

	cout << endl
		 << "Volume of box1 = " << boxVolume;

	cout << endl
		 << "Volume of box2 = "
		 << box2.Volume();
		

	cout << endl
		 << "A CBox object occupies "
		 << sizeof box1 << "bytes.";

	cout << endl;
	return 0;

}
           
Visual C++ 2010 第7章 自定義資料類型

7.3.5 成員函數定義的位置

成員函數可以放在類定義的外部,但需要将函數原型放在類内部。

class CBox
{
public:
	double m_Length;
	double m_Width;
	double m_Height;
	double Volume(void);
};
//函數名加上類名作為字首,并用作用域解析運算符::将二者分開
// 告訴編譯器該函數屬于CBox類
double CBox::Volume ()
{
	return m_Length*m_Width*m_Height;
}
           

7.3.6 内聯函數

在内聯函數中,編譯器設法以函數體代碼代替函數調用。這樣可以避免調用函數時的大量系統開銷。

Visual C++ 2010 第7章 自定義資料類型

如果函數定義位于類定義外部時, 在函數頭前面加上關鍵字inline,即告訴編譯器将函數視為内聯函數。

inline double CBox::Volume()
{
	return m_Length*m_Width*m_Height;
}
           
  • 注意,内聯函數 最适合用于短小的簡單程式。

7.4 類構造函數

7.4.1 類構造函數的概念

  • 類構造函數是類的特殊函數,在建立新的類對象時調用它。
  • 類構造函數提供了建立對象時進行初始化的機會,并確定資料成員隻包含有效值。
  • 類可以有多個構造函數,以允許我們以不同方式建立對象。
  • 類個構造函數總是與所屬類的名稱相同。
  • 構造函數沒有任何傳回類型,給構造函數指定傳回類型是錯誤的,即使寫成void 也不行。
#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	double m_Length;
	double m_Width;
	double m_Height;
	// CBox類的構造函數
	CBox(double lv,double bv,double hv)
	{
		cout << endl << "Constructor called."; //此句輸出可以辨識調用構造函數的時間,常用于測試程式
		m_Length = lv;
		m_Width = bv;
		m_Height = hv;
	}
	// CBox類的成員函數
	double Volume()
	{
		return m_Length*m_Width*m_Height;
	}
};

int main()
{ 
	CBox box1(78.0,24.0,18.0);   //聲明并初始化CBox類的執行個體
	CBox cigarBox(8.0,5.0,1.0);

	double boxVolume(0.0);

	boxVolume = box1.Volume();

	cout << endl
		 << "Volume of box1 = " << boxVolume;

	cout << endl
		 << "Volume of cigarBox = "
		 << cigarBox.Volume();

	cout << endl;
	return 0;

}
           
Visual C++ 2010 第7章 自定義資料類型

7.4.2 預設的構造函數

#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	double m_Length;
	double m_Width;
	double m_Height;
	// CBox類的構造函數
	CBox(double lv,double bv,double hv)
	{
		cout << endl << "Constructor called."; //此句輸出可以辨識調用構造函數的時間,常用于測試程式
		m_Length = lv;
		m_Width = bv;
		m_Height = hv;
	} 
	// 預設構造函數 :無實參構造函數,被調用時不要求提供實參
	CBox()
	{
		cout << endl << "Default constructor called.";
	}
	// CBox類的成員函數
	double Volume()
	{
		return m_Length*m_Width*m_Height;
	}
};

int main()
{ 
	CBox box1(78.0,24.0,18.0);   //聲明并初始化CBox類的執行個體
	CBox box2;					 //聲明box2 未初始化
	CBox cigarBox(8.0,5.0,1.0);

	double boxVolume(0.0);

	boxVolume = box1.Volume();

	cout << endl
		 << "Volume of box1 = " << boxVolume;

	box2.m_Height = box1.m_Height - 10;
	box2.m_Length = box1.m_Length/2.0;
	box2.m_Width = 0.25*box1.m_Length;

	cout << endl
		 << "Volume of box2 = "
		 << box2.Volume();

	cout << endl
		 << "Volume of cigarBox = "
		 << cigarBox.Volume();

	cout << endl;
	return 0;
}
           
Visual C++ 2010 第7章 自定義資料類型

7.4.3 在類定義中指定預設的形參值

  • 将成員函數的定義放在類定義内部,将形參的預設值放在函數頭中。
  • 類定義中僅包括函數原型,則預設形參值應該放在原型中。
class CBox
{
public:
	double m_Length;
	double m_Width;
	double m_Height;
	// CBox類的構造函數
	CBox(double lv = 1.0,double bv = 1.0 ,double hv = 1.0)  // 構造函數頭中設定預設值
	{
		cout << endl << "Constructor called."; 
		m_Length = lv;
		m_Width = bv;
		m_Height = hv;
	} 
	CBox()
	{
		cout << endl << "Default constructor called.";
	}
	double Volume()
	{
		return m_Length*m_Width*m_Height;
	}
};
           

7.4.4 在構造函數中使用初始化清單

CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0):
			m_Length(lv),m_Width (bv),m_Height (hv)
{
	cout << endl << "Constructor called."; 
}
           

7.4.5 聲明顯式的構造函數

構造函數聲明為explicit ,則隻能顯式的調用它,且不用于隐式轉換。

explicit CBox (double side):m_Length(side),m_Width (side),m_Height (side)
{ }
           

7.5 類的私有成員

  • private 類成員隻能被類的成員函數通路。
  • 指定private類成員 能夠将類的接口與類的内部實作分開
  • 類的接口由public成員,特别是public成員函數組成,因為它們可以提供對包括private成員在内的所有類成員的間接通路。
    Visual C++ 2010 第7章 自定義資料類型
#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	//構造函數聲明為explicit ,避免不期望的隐式轉換
	explicit CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0):
		m_Length(lv),m_Width(bv),m_height(hv)
		{
			cout << endl << "Constructor called.";
		}
	double Volume()
	{
		return m_Length*m_Width*m_height;
	}
private: //3個資料成員隻能被本類的成員函數通路
	double m_Length;
	double m_Width;
	double m_height;
};
// 現在使用構造函數或成員函數是給對象的私有資料成員指派的唯一方法
int main()
{
	CBox match(2.2,1.1,0.5);
	CBox box2;

	cout << endl
		 << "Volume of match = "
		 << match.Volume();

	cout << endl
		 << "Volume of box2 = "
		 << box2.Volume();
	cout << endl;

	return 0;
}
           
Visual C++ 2010 第7章 自定義資料類型

7.5.1 通路私有類成員

傳回資料成員值的成員函數:

inline double CBox::GetLength()
{
	return m_Length;
}
// 類的public 部分聲明了該函數後,通過如下語句來使用:
double len = box2.GetLength();
           

需要為每個希望在外部使用的資料成員編寫一個類似的函數,這樣既可以通路這些成員的值又不會危及類的安全性。

7.5.2 類的友元函數

  • 某些雖然不是類成員的函數能夠通路類的所有成員——它們擁有特殊權限,這樣的函數稱為類的友元函數,使用friend來定義。
  • 可以在類定義中添加友元函數的原型,也可以條件整個函數定義。在類定義内定義的友元函數預設也是内聯函數。
  • 友元函數不是類的成員,是以通路特性不适用于它們,這些函數隻是擁有特殊權限的普通全局函數。
#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	//構造函數聲明為explicit ,避免不期望的隐式轉換
	explicit CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0)
	{
		cout << endl << "Constructor called.";
		m_Length = lv;
		m_Width = bv;
		m_height = hv;
	}
	double Volume()
	{
		return m_Length*m_Width*m_height;
	}
private: //3個資料成員隻能被本類的成員函數通路
	double m_Length;
	double m_Width;
	double m_height;
friend double BoxSurface(CBox aBox); //聲明CBox類的友元

// 友元函數放在類定義内部,影響程式可讀性,不建議使用
/*friend double BoxSurface(CBox aBox)
{
	return 2.0*(aBox.m_Length * aBox.m_Width + 
				aBox.m_Length * aBox.m_height +
				aBox.m_height * aBox.m_Width);
}*/
};

double BoxSurface(CBox aBox)
{
	return 2.0*(aBox.m_Length * aBox.m_Width + 
				aBox.m_Length * aBox.m_height +
				aBox.m_height * aBox.m_Width);
}

int main()
{
	CBox match(2.2,1.1,0.5);
	CBox box2;

	cout << endl
		 << "Volume of match = "
		 << match.Volume();

	cout << endl
		 << "Surface area of match = "
		 << BoxSurface(match);

	cout << endl
		 << "Volume of box2 = "
		 << box2.Volume();

	cout << endl
		 << "Surface area of box2 = "
		 << BoxSurface(box2);
	cout << endl;

	return 0;
}
           
Visual C++ 2010 第7章 自定義資料類型

7.5.3 預設複制構造函數

#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	//構造函數聲明為explicit ,避免不期望的隐式轉換
	explicit CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0)
	{
		cout << endl << "Constructor called.";
		m_Length = lv;
		m_Width = bv;
		m_height = hv;
	}
	double Volume()
	{
		return m_Length*m_Width*m_height;
	}
private: //3個資料成員隻能被本類的成員函數通路
	double m_Length;
	double m_Width;
	double m_height;
};

int main()
{
	CBox box1(78.0,24.0,18.0);
	CBox box2 = box1; 
	//通過用同類的現有對象進行初始化來建立類對象,複制構造函數

	cout << endl
		 << "box1 volume = " << box1.Volume()
		 << endl
		 << "box2 volume = " << box2.Volume();
	cout << endl;

	return 0;
}
           

7.6 this 指針

  • 任何成員函數執行時,都自動包含一個名為this的隐藏指針,它指向調用該函數時使用的對象。

    Volume()函數在執行期間通路m_Length成員時,實際上是在引用this->m_Length (被使用的對象成員的全稱)。編譯器負責在函數中給成員名添加必要的指針名this。

顯示的使用this:

#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	//構造函數聲明為explicit ,避免不期望的隐式轉換
	explicit CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0)
	{
		cout << endl << "Constructor called.";
		m_Length = lv;
		m_Width = bv;
		m_height = hv;
	}
	double Volume()
	{
		return m_Length*m_Width*m_height;
	}

	bool Compare(CBox &xBox)
	{
		return this->Volume() > xBox.Volume();
		// return Volume() > xBox.Volume(); 換成此句 仍能正常工作
		// 任何對不加限定的成員名的引用,編譯器将自動認為是引用this指向的那個對象成員
	}
private: //3個資料成員隻能被本類的成員函數通路
	double m_Length;
	double m_Width;
	double m_height;
};

int main()
{
	CBox match(2.2,1.1,0.5);
	CBox cigar(8.0,5.0,1.0);

	if(cigar.Compare(match))
		cout << endl
			 << "match is smaller than cigar";
	else
		cout << endl
			 << "match is equal to or larger than cigar"; 
	
	cout << endl;
	return 0;
}
           

通過對象的指針通路成員時使用間接成員通路操作符 ->.

7.7 類的const對象

将某個CBox 對象定義成const :

const CBox standard(3.0,5.0,8.0);

将某個類對象聲明為const,則編譯器将不允許對象調用任何可能修改它的成員函數。

7.7.1 類的const 成員函數

為了使成員函數中的this 指針稱為const,必須在類定義内将該函數聲明為const。

class CBox
{
public:
	explicit CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0)
	{
		cout << endl << "Constructor called.";
		m_Length = lv;
		m_Width = bv;
		m_height = hv;
	}
	double Volume() const 
	{
		return m_Length*m_Width*m_height;
	}

	bool Compare(const CBox &xBox) const
	{
		return this->Volume() > xBox.Volume();
	}
private: 
	double m_Length;
	double m_Width;
	double m_height;
};
           
  • 要指定const 成員函數,隻需在函數頭後面附加const關鍵字即可。注意,隻能對類成員函數這麼做。
  • 作用:使該函數中的this指針稱為const,意味着不能在該函數的定義内在指派語句左邊寫上類的資料成員。
  • const成員函數不能調用同類的非const成員函數。

    Compare() 調用 Volume() ,是以Volume()成員也必須聲明為const。由于Volume()聲明為const,是以可以使Compare()函數的形參為const。

  • 當将某個對象聲明為const 之後,該對象可以調用的成員函數也都必須聲明為const。

7.7.2 類外部成員函數定義

當const成員函數的定義出現在類外部時,函數頭必須添加const。

class CBox
{
public:
	explicit CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0);
	double Volume() const;
	bool Compare(const CBox &xBox) const;
private:
	double m_Length;
	double m_Width;
	double m_height;
};
double CBox::Volume() const
	{
		return m_Length*m_Width*m_height;
	}
bool CBox::Compare(CBox &xBox) const
	{
		return this->Volume() > xBox.Volume();
	}
CBox::CBox(double lv,double bv,double hv):
	m_Length(lv),m_Width(bv),m_height(hv)
{
	cout << endl << "Constructor called.";
}
           

7.8 類對象的數組

類對象數組的每個元素都将調用預設構造函數。

#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0)
	{
		cout << endl << "Constructor called.";
		m_Length = lv;
		m_Width = bv;
		m_height = hv;
	}
	CBox()
	{
		cout << endl
			 << "Default constructor called.";
		m_Length = m_Width = m_height = 1.0;
	}
	double Volume() const
	{
		return m_Length*m_Width*m_height;
	}
private: //3個資料成員隻能被本類的成員函數通路
	double m_Length;
	double m_Width;
	double m_height;
};

int main()
{
	CBox boxes[5];
	CBox cigar(8.0,5.0,1.0);

	cout << endl 
		 << "Volume of boxes[3] = " << boxes[3].Volume()
		 << endl
		 << "Volume of cigar = " << cigar.Volume();

	cout << endl;
	return 0;
}
           

7.9 類的靜态成員

7.9.1 類的靜态資料成員

将類的某個資料成員聲明為static時,将隻能定義一次該靜态資料成員,而且要被同類的所有對象共享。每個靜态資料成員隻有一個執行個體存在。

Visual C++ 2010 第7章 自定義資料類型
  • 靜态資料成員的用途之一:統計實際存在多少個對象。
  • 添加靜态資料成員:

    static int objectCount;

  • 初始化靜态資料成員:在類定義外部進行初始化:

    int CBox::objectCount = 0;

#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
	static int objectCount; //定義靜态資料成員

	CBox(double lv = 1.0,double bv = 1.0,double hv = 1.0)
	{
		cout << endl << "Constructor called.";
		m_Length = lv;
		m_Width = bv;
		m_height = hv;
		objectCount++;
	}
	CBox()
	{
		cout << endl 
			 << "Default constructor called.";
	    m_Length = m_Width = m_height = 1.0;
		objectCount++;
	}

	double Volume() const
	{
		return m_Length*m_Width*m_height;
	}

private: //3個資料成員隻能被本類的成員函數通路
	double m_Length;
	double m_Width;
	double m_height;
};

int CBox::objectCount(0); //初始化靜态資料成員

int main()
{
	//CBox boxes[5];
	CBox cigar(8.0,5.0,1.0);

	cout << endl << endl 
		 << "Number of objects(through class) = "
		 << CBox::objectCount;
	
	cout << endl;
	return 0;
}
           

7.9.2 類的靜态函數成員

  • 将某個函數成員聲明為static ,将使該函數獨立于本類的任何具體對象。
  • 靜态成員函數優點:即使本類的任何對象都不存在,它們也能存在并被調用。這種情況下,靜态成員函數隻能使用靜态資料成員,因為後者是唯一存在的資料成員。
  • 靜态函數原型:static void Afunction(int n);
  • 通過對象調用靜态函數:aBox.Afunction(10);
  • 不通過對象調用靜态函數:CBox::Afunction(10);

7.10 類對象的指針和引用

7.10.1 類對象的指針

  • 聲明指向CBox類對象的指針: CBox* pBox(nullptr);
  • 用該指針存儲CBox對象的位址: pBox = &cigar;
  • 使用對象的指針調用函數:cout << pBox->Volume();

7.10.2 類對象的引用

  • 聲明對象cigar的引用:CBox& rcigar(cigar);
  • 使用引用計算對象cigar的體積,隻需在應該出現對象名的位置适應引用名即可:

    cout << rcigar.Volume();

實作複制構造函數

  • 複制構造函數的原型:

    CBox(const CBox& initB);

    函數的形參是引用,則調用該函數時不需要複制實參。函數直接通路調用函數中的實參變量。const 限定符用來確定該函數不能修改實參。

  • 實作複制構造函數:

    CBox::CBox(const CBox& initB)

    {

    m_Length = initB.m_Length ;

    m_Width = initB.m_Width ;

    m_Height = initB.m_Height ;

    }

7.11 C++/CLI 程式設計

  • 在C++/CLI 中結構和類的差別:

    結構成員預設都是公有的;

    類成員預設都是私有的;

  • 注意:

    1.C++/CLI類的成員函數不能聲明為const。

    2.在值類類型T的非靜态函數成員中,this指針是interior_ptr< T > 類型的内部指針,而在引用類類型T中,this指針是T^ 類型的句柄。

  • 值類和引用類的3項其他限制:
  • 值類或引用類不能包含是本地C++數組或本地C++類的字段。
  • 友元函數是不允許的。
  • 值類或引用類不能有位字段成員。

7.11.1 定義值類類型

  • value struct 和 value class類型的差別:

    value struct 類型的成員預設都是公有的;

    value class 類型的成員預設都是私有的;

value class Height // 定義名為Height的值類類型
{
private:
	int feet;
	int inches;

public:
	// 根據提供的實參——英寸數建立Height對象的構造函數
	Height(int ins) 
	{
		feet = ins/12;
		inches = ins%12;
	}
	// 根據英尺和英寸兩項 建立Height對象的構造函數
	Height(int ft,int ins):feet(ft),inches(ins){}
};
// 建立 Height類型的變量:
Height tall = Height(7,8);

Height baseHeight; // 該語句建立的變量将自動初始化為高度0.
           
  • Height類沒有指定的無參數構造函數,因為它是值類,不允許在類定義中提供那樣的構造函數,值類中将自動包括一個無參數的構造函數,該函數将把所有值類型的字段初始化為0, 把所有句柄字段初始化為nullptr,不能重寫這個隐含的構造函數。
  • 關于值類的2項限制:

    1.值類定義中不能包括指派構造函數;

    2.不能在值類中重寫指派運算符;

Height myHeight(Height(6,3)); //棧變量
	Height^ yourHeight(Height(70)); // Height^ 類型的句柄 ,堆變量
	Height hisHeight(*yourHeight); 
	// 棧變量,是yourHeight引用對象的副本,是以要先解除引用才能将其指派給hisHeight
           

1. 類的ToString()函數

  • ToString()函數:
    • 函數傳回一個用來表示類對象的字元串的句柄。
    • 編譯器隻要認為需要某個對象的字元串表示法,就安排調用ToString()函數。
double pi(3.142);
Console::WriteLine(pi.ToString());
           

該語句以字元串的形式輸出pi的值,如果不顯式的調用ToString()函數,也能得到同樣的輸出。

value class Height // 定義名為Height的值類類型
{
private:
	int feet;
	int inches;

public:
	// 根據提供的實參——英寸數建立Height對象的構造函數
	Height(int ins) 
	{
		feet = ins/12;
		inches = ins%12;
	}
	// 根據英尺和英寸兩項 建立Height對象的構造函數
	Height(int ft,int ins):feet(ft),inches(ins){}

	virtual String^ ToString() override
	{
		return feet + L" feet"+inches + L" inches";
	}
};
           

virtual 關鍵字 結合函數形參清單後的override關鍵字,辨明該版本的ToString()函數重寫了類中預設給出的函數版本。

2.字面值字段

  • C++/CLI 具有的 字面值字段功能可以給類中引入命名常量。
value class Height // 定義名為Height的值類類型
{
private:
	int feet;
	int inches;
	literal int inchesPerFoot = 12; //使用inchesPerFoot代替12
public:
	// 根據提供的實參——英寸數建立Height對象的構造函數
	Height(int ins) 
	{
		feet = ins/inchesPerFoot;
		inches = ins%inchesPerFoot;
	}
	// 根據英尺和英寸兩項 建立Height對象的構造函數
	Height(int ft,int ins):feet(ft),inches(ins){}

	virtual String^ ToString() override
	{
		return feet + L" feet"+inches + L" inches";
	}
};
           
  • 注意,不能使用函數表示法來初始化字面值字段。

7.11.2 定義引用類類型

  • 引用類在功能上相當于本地C++類,而且沒有值類收到的那些限制。但與本地C++類不同,引用類沒有預設的複制構造函數或預設的指派運算符。
  • 在C++/CLI類中不推薦使用:類名的C字首 和 成員名的m_字首。
  • 不能在C++/CLI 類中給函數和構造函數的形參指定預設值,是以必須給Box類添加無參數的構造函數來實作該功能。
#include "stdafx.h"

using namespace System;

ref class Box
{
public:
	Box():Length(1.0),Width(1.0),Height(1.0) //無參數構造函數
	{
		Console::WriteLine(L"No-arg constructor called.");
	}

	Box(double lv,double bv,double hv):
				Length(lv),Width(bv),Height(hv)
	{
		Console::WriteLine(L"Constructor called.");	
	}

	double Volume()
	{
		return Length*Width*Height;
	}
private:
	double Length;
	double Width;
	double Height;
};


int main(array<System::String ^> ^args)
{
	Box^ aBox; //建立跟蹤句柄aBox 預設初始化為nullptr
	Box^ newBox = gcnew Box(10,15,20);  //建立新的Box對象,并将對象的位址存入句柄newBox中
	aBox = gcnew Box; //調用無參數的構造函數建立Box對象,并将對象的位址存入句柄aBox中
	Console::WriteLine(L"Default box volume is {0}",aBox->Volume());	
	Console::WriteLine(L"New box volume is {0}",newBox->Volume());

	Console::ReadLine();
    return 0;
}
           
Visual C++ 2010 第7章 自定義資料類型

7.11.3 定義引用類類型的複制構造函數

  • 複制構造函數的形參必須為const引用。
  • 定義Box類的複制構造函數:
Box(const Box% box):Length(box->Length),Width(box->Width),Height(box->Height)
{}
           
  • 允許将T類型的引用類型對象按值傳遞給函數的引用類T的複制構造函數的形式:
T(const T% t)
{
	//code to make copy
}
           

一個采用句柄做實參的複制構造函數:

Box (const Box^ box):Length(box->Length),Width(box->Width),Height(box->Height)
{}
           

7.11.4 類屬性

  • 屬性是值類或引用類的成員,可以把屬性當作字段來通路,但它們實際上不是字段。
  • 屬性與字段的首要差別:字段名是引用某個存儲位置,屬性名是調用某個函數。
  • 屬性擁有分别擷取和設定屬性值的get() 和 set()通路器函數。
    • 當使用屬性名擷取屬性值時,背景是在調用該屬性的get()函數;
    • 當在指派語句左邊使用屬性名時,實際上是在調用該屬性的set()函數。
    • 如果某個屬性隻提供了get()函數的定義,則稱為隻讀屬性。
    • 某個屬性隻有set()函數的定義,稱為隻寫屬性。
  • 類可以包含2種不同的屬性:标量屬性和索引屬性。
    • 标量屬性:可以通過屬性名通路的單值;
    • 索引屬性:是一組值,需要在屬性名後面的方括号内使用索引來通路。

1.定義标量屬性

  • 标量屬性是一個單值,在類定義中使用property關鍵字定義标量屬性。
    • 标量屬性的get()函數必須有與屬性類型相同的傳回類型
    • set()函數必須有其類型與屬性相同的形參。
value class Height
{
private:
	int feet;
	int inches;
	literal int inchesPerFoot = 12;
	literal double inchesToMeters = 2.54/100;

public:
	Height(int ins)
	{
		feet = ins / inchesPerFoot;
		inches = ins % inchesPerFoot;
	}

	Height(int ft,int ins):feet(ft),inches(ins){}

	property double meters //定義名為meters的屬性
	{
		double get()
		{
			return inchesToMeters*(feet*inchesPerFoot+inches);
		}
	}

	virtual String^ToString() override
	{
		return feet + L" feet" + inches + L" inches";
	}
};
// 通路屬性
Height ht(Height(6,8));
Console::WriteLine(L"The height is {0} meters",ht.meters);
           

在類定義外部定義get() \ set()函數:

value class Height
{
// code as before...
public:
// code as before...
	property double meters //定義名為meters的屬性
	{
		double get();
	}
// code as before...
};
// Height 表明該函數屬于Height類;meters 表明該函數屬于類中的meters屬性
double Height::meters::get()
{
	return inchesToMeters*(feet*inchesPerFoot+inches);
}
           

2. 普通标量屬性

在定義類的标量屬性時不提供get()和set()函數的定義,這樣的屬性被稱作普通标量屬性。 要指定普通标量屬性,隻需省略包含get()和set()函數定義的大括号,并以 ; 結束屬性聲明。

value class Point
{
public:
	property int x;
	property int y;
	
	virtual String^ ToString() override
	{
		return L "(" + x + L"," + y + L")";
	}
}
           

編譯器自動為每個普通标量屬性提供預設的 get()和set()函數定義。get() 将傳回屬性值,set()将把屬性值設定為與該屬性類型相同的實參。

#include "stdafx.h"

using namespace System;

value class Height
{
private:
	int feet;
	int inches;
	literal int inchesPerFoot = 12;
	literal double inchesToMeters = 2.54/100;
public:
	Height(int ins)
	{
		feet = ins / inchesPerFoot;
		inches = ins % inchesPerFoot;
	}

	Height(int ft,int ins):feet(ft),inches(ins){}

	property double meters //定義名為meters的屬性
	{
		double get()
		{
			return inchesToMeters*(feet*inchesPerFoot+inches);
		}
	}
	virtual String^ToString() override
	{
		return feet + L" feet" + inches + L" inches";
	}
};

value class Weight
{
private:
	int lbs;
	int oz;
	literal int ouncesPerPound = 16;
	literal double lbsToKg = 1.0/2.2;

public:
	Weight(int pounds,int ounces)
	{
		lbs = pounds;
		oz = ounces;
	}

	property int pounds
	{
		int get(){return lbs;}
		void set(int value){lbs = value;}
	}

	property int ounces
	{
		int get(){return oz;}
		void set(int value){oz = value;}
	}

	property double kilograms
	{
		double get(){return lbsToKg * (lbs + oz/ouncesPerPound);}
	}

	virtual String^ToString() override
	{
		return lbs + L" pounds" + oz + L" ounces";
	}
};

ref class Person
{
private:
	Height ht;
	Weight wt;
public:
	property String^ Name; // Name是普通屬性
	Person(String^ name,Height h,Weight w):ht(h),wt(w)
	{
		Name = name;
	}

	property Height height
	{
		Height get(){return ht;}
	}

	property Weight weight
	{
		Weight get(){return wt;}
	}
};

int main(array<System::String ^> ^args)
{
	Weight hisWeight(185,7);
	Height hisHeight(6,3);
	Person^ him(gcnew Person(L"Fred",hisHeight,hisWeight));

	Weight herWeight(Weight(105,3));
	Height herHeight(5,2);
	Person^ her(gcnew Person(L"Freda",herHeight,herWeight));

	Console::WriteLine(L"She is {0}",her->Name);
	Console::WriteLine(L"Her weight is {0:F2} kilograms.",her->weight.kilograms);
	Console::WriteLine(L"Her height is {0} which is {1:F2} meters.",her->height,her->height.meters);

	Console::WriteLine(L"He is {0}",him->Name);
	Console::WriteLine(L"His weight is {0:F2} kilograms.",him->weight.kilograms);
	Console::WriteLine(L"His height is {0} which is {1:F2} meters.",him->height,him->height.meters);

    Console::ReadLine();
    return 0;
}
           

3.定義索引屬性

  • 索引屬性是類的一組屬性值,通過在引用對象的變量名後面加上包含索引的[ ]來通路。
  • 擁有屬性名的索引屬性稱為命名索引屬性。
ref class Name
{
private:
	array <String^>^ Names;
public:
	Name(...array<String^>^ names): Names(names){}

	property String^ default[int] // default說明是預設索引屬性
	{
		String^ get(int index)
		{
			if(index >= Names->Length)
				throw gcnew Exception(L"Index out of range");
			return Names[index];
		}
	}
};
           
  • default關鍵字後面的方括号表明,該屬性實際上是索引屬性。[ ] 包圍的類型是擷取屬性值時要使用的索引值的類型。
  • 對于使用當索引通路的索引屬性來說,get()函數必須有一個類型與屬性名後面方括号内出現的類型 相同的形參,以用來指定索引。
  • 索引屬性的 set()函數必須有2個形參:第1個是 索引;第2個 是用來設定第1個形參對應的屬性的新值。
#include "stdafx.h"

using namespace System;

ref class Name
{
private:
	array <String^>^ Names;
public:
	Name(...array<String^>^ names): Names(names){} //形參清單以省略号開始,可接受任意數量的實參

	property int NameCount //NameCount 标量屬性
	{
		int get(){return Names->Length;}
	}

	property String^ default[int] // default說明是預設索引屬性
	{
		String^ get(int index)
		{
			if(index >= Names->Length)
				throw gcnew Exception(L"Index out of range");
			return Names[index];
		}

		void set(int index,String^ name) 
		{
			if(index >= Names->Length)
				throw gcnew Exception(L"Index out of range");
			Names[index] = name;
		}
	}
};
int main(array<System::String ^> ^args)
{
	Name^myName = gcnew Name(L"Ebenexer",L"Isaiah",L"Ezra",L"Inigo",L"Whelkwhistle");
	myName [myName->NameCount - 1]= L"Oberwurst";

	for(int i=0 ;i < myName->NameCount ; i++)
		Console::WriteLine(L"Name {0} is {1}",i+1,myName[i]);

    Console::ReadLine();
    return 0;
}

           

給Name類添加一個命名索引屬性:

ref class Name
{
	// Code as before
	property wchar_t Initials[int] //該索引屬性名為Initials
	{
		wchar_t  get(int index)
		{
			if(index >= Names->Length)
				throw gcnew Exception(L"Index out of range");
			return Names[index] [0];
		}
	}
}
           

4. 更複雜的索引屬性

将索引屬性定義成要求使用多個索引才能通路屬性值,屬性可以不是數字。

enum class Day{Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday};

ref class Shop
{
public:
	property String^ Opening[Day,String^]
	{
		String^ get(Day day,String^ AmOrPm)
		{
			switch(day)
			{
			case Day::Saturday:
				if(L"am"== AmOrPm)
					return L"9:00";
				else 
					return L"14:30";
				break;
			case Day::Sunday:
				return L"closed";
				break;
			default:
				if(L"am" == AmOrPm)
					return L"9:30";
				else 
					return L"14:00";
				break;
			}
		}
	}
};

int main(array<System::String ^> ^args)
{
	Shop^ shop(gcnew Shop);
	Console::WriteLine(shop->Opening[Day::Saturday,L"pm"]);
    Console::ReadLine();
    return 0;
}
           

5. 靜态屬性

  • 使用static 建立靜态屬性
value class Height
{
public:
	static property String^ Units
	{
		String^ get(){return L"feet and inches";}
	}
};
           
  • 通過使用以類名限定的屬性名來通路靜态屬性:

    Console::WriteLine(L"Class units are {0}.",Height::Units);

  • 如果已經定義過類對象,則可以使用變量名通路靜态屬性:

    Console::WriteLine(L"Class units are {0}.",myHt.Units);

  • 如果要通過對象句柄通路引用類中的靜态屬性,則應該使用->操作符。

6.屬性的保留名稱

  • 雖然屬性與字段不同,但屬性仍然必須存儲在一地方,而存儲位置也需要以某種方式辨別。在内部,屬性包含幾個為所需的存儲位置建立的名稱,它們在包含屬性的類中屬于保留名稱,是以決不能為其他目的而使用它們。
  • 如果在類中用名稱NAME定義标量或命名索引屬性,則get_NAME 和 set_NAME是奔雷的保留名稱。
  • 無論是否為NAME屬性定義get() 和 set() 函數,get_NAME 和 set_NAME 兩個名稱都是保留名稱。
  • 當在類中定義預設索引屬性時,get_Item 和 set_Item 是保留名稱。

注意,C++/CLI 程式中可能存在使用下劃線字元的保留名稱,是以我們應該避免在自定義的名稱中使用下劃線。

7.11.5 initonly 字段

C++/CLI 提供了再構造函數中進行初始化的initonly 常量類字段。

value class Height
{
private:
	int feet;
	int inches;
public:
	initonly int inchesPerFoot; 

	Height(int ft,int ins):
	feet(ft),inches(ins),inchesPerFoot(12){}
};
           
  • initonly 字段在初始化以後不能再被修改,它始終是固定不變的。
  • 注意,決不能再聲明非靜态initonly 字段時指定初始值,而必須在構造函數中初始化所有的非靜态initonly 字段。

還可以在構造函數體中,初始化非靜态initonly 字段

Height(int ft,int ins):
	feet(ft),inches(ins),
	{ inchesPerFoot = 12;}
           

可以将類的initonly 字段定義為static,這樣該字段将被所有類成員共享。如果它還是公有字段,使用以類名限定的字段名就可以通路它。

value class Height
{
private:
	int feet;
	int inches;
public:
	initonly static int inchesPerFoot = 12; 

	Height(int ft,int ins):
	feet(ft),inches(ins)
	{ }
};
           

7.11.6 靜态構造函數

  • 靜态構造函數是使用static 關鍵字聲明的構造函數,用來初始化靜态字段 和靜态initonly 字段。
  • 靜态構造函數沒有形參,也不能有初始值設定清單。
  • 靜态構造函數總是私有的,與是否将其放在類的公有部分無關。
  • 不能直接調用靜态構造函數,它将在普通構造函數執行之前被自動調用。
  • 任何聲明中制定了初始值的靜态字段,都将在靜态構造函數執行之前被初始化。
value class Height
{
private:
	int feet;
	int inches;

	static Height(){ inchesPerFoot = 12;}

public:
	initonly static int inchesPerFoot;

	Height(int ft,int ins):
	feet(ft),inches(ins)
	{ }
};
           

7.14 本章主要内容

Visual C++ 2010 第7章 自定義資料類型
Visual C++ 2010 第7章 自定義資料類型

To be continue…