# 構造函數

在宣告一個對象後,緊接著要給對象初始化,對象初始化實質上就是對所有數據成員賦值。
如果對象中某個數據成員未賦值,則該數據成員的值不確定,那麼該對象的值不完整。
構造函數 Constructor 用於創建一個對象,提供了初始化該對象的一種簡便手段。

<類名> (<參數表>)
{
	<函數體>
}

注意:

  • 構造函數的函數名必須與類名相同
  • 構造函數沒有返回值
  • 其功能是將對象中的所有數據成員進行初始化,一般由一系列賦值語句構成
  • 由系統在宣告對象時自動調用

# 有無構造函數的區別

在前面章節中的程序宣告變數

int a=0; 
int a(0);
struct Date today={2013,3,19};
struct Date today(2013,3,19);

以上為變數宣告的同時賦初值,即都允許初始化。

  • 對於無構造函數的類,宣告變數(對象)不允許初始化
CLOCK alarm={10,53,11}; // 錯誤
CLOCK alarm(10,53,11); // 錯誤
  • 有構造函數就允許初始化
Date birthday(1998,12,12); // 正確
Date birthday={1998,12,12}; // 錯誤

# 重載構造函數

  • 一個類中允許定義多個構造函數
  • 在宣告對象時自動選擇執行某個構造函數
    數據成員初始化的四種方法:
  1. 在構造函數的函數體中進行初始化
Date(Date &d)
{
	year=d.year;
	month=d.month;
	day=d.day;
}
  1. 在構造函數的頭部初始化
<類名>::<構造函數名>(<參數表>):<變數1>(<初值1>),<變數n>(<初值n>){}
Date::Date():year(1900),month(1),day(1)
{
}
  1. 混合初始化:前兩種方式結合
Date::Date(int y,int m,int d):year(y),month(m)
{
	day=d;
}
  1. 使用默認參數初始化
Date(char yy[], int mm = 1, int dd = 1)
{
	year = atoi(yy);
	month = mm;
	day = dd;
}

# 例子:日期類中 4 個重載構造函數

#include<iostream>
#include<string.h>
#include<stdlib.h>
using namespace std;
class Date
{
    int year,month,day;
    public:
        // 無參構造函數定義
        Date():year(1900),month(1),day(1) { }
        // 默认参数构造函数
        Date(int yy,int mm = 1,int dd = 1);
        // 日期對象參數構造函數
        Date(Date &d):year(d.year),month(d.month),day(d.day){};
        // 字元串日期構造函數
        Date(char *ps);
        void print_ymd();
    };
    // 默認參數構造函數的定義
    Date::Date(int yy,int mm,int dd):year(1900),month(1),day(1)
    {
    if (yy>=1900&&yy<=9999)
    {
        year = yy;
    }
    else
    {
        return;
    }
    if (mm>=1&&mm<=12)
    {
        month = mm;
    }
    else
    {
        year=1900;
        return;
    }
    if (dd>=1&&dd<=31)
    {
        day = dd;
    }
    else
    {
        year=1900;
        month=1;
        return;
    }
    };
    // 重載構造函數的定義
    Date::Date(char *ps):year(1900),month(1),day(1)
    {
    char py[5],pm[3],pd[3];
    strncpy(py,ps,4);
    ps=ps+5;
    strncpy(pm,ps,2);
    ps=ps+3;
    strncpy(pd,ps,2);
    int yy=atoi(py),mm=atoi(pm),dd=atoi(pd);
    if (yy>=1900&&yy<=9999)
    {
        year = yy;
    }
    else
    {
        return;
    }
    if (mm>=1&&mm<=12)
    {
        month = mm;
    }
    else
    {
        year=1900;
        return;
    }
    if (dd>=1&&dd<=31)
    {
        day = dd;
    }
    else
    {
        year=1900;
        month=1;
        return;
    }
}

# 析構函數

構造函數 Constructor :在對象創建時執行,提供了初始化對象的一種簡便手段。
析構函數 Destructor :在對像被撤銷時 (前) 執行,用於完成對像被撤銷前的一些清理工作。

具體地說,析構函數往往用於釋放 “資源”,如在構造函數中動態申請的內存空間。
也可以被用來執行 “用戶希望在最後一次使用對象之後所執行的任何操作”,例如輸出有關信息等。

# 對象的存儲空間

每個對象中的數據成員分配了存儲空間。
所有對象中的函數成員共用一個存儲空間,在代碼區存放。

# 定義方式

<類名>::~<類名>()
{
	<函數體>
}
  • 注意:
    • 函數名是類名前面加 ~ 符號
    • 析構函數不得返回任何值
    • 析構函數不得帶有任何參數
    • 其主要功能是在撤銷對象之前進行一些善後處理工作
    • 由系統自動調用
  • 例子:日期類中增加析構函數
#include<iostream>
using namespace std;
class Date
{
	int year,month,day;
	public:
		// 無參構造函數
		Date():year(1900),month(1),day(1) { }
		// 默認參數構造函數
		Date(int yy,int mm = 1,int dd = 1);
		// 日期對象構造函數
		Date(Date &d):year(d.year),month(d.month),day(d.day){};
		// 字元串日期構造函數
		Date(char *ps);
		void print_ymd();
		~Date() { }; // 析構函數定義
		// 由於析構函數是空函數 (函數體中沒有一條語句),沒有感覺到析構函數被自動運行
};

# 實際意義的析構函數

機器人的特徵 
    姓名 
    型號 
    整數:待翻譯的整數 
    翻譯的英文句子字符串:字符指針,句子有長有短 
    …… 
機器人的功能 
    翻譯整數函數:形成英文字符串,並將字符串指針返回 
    構造函數 
    設置函數 
    輸出英文句子函數 
    析構函數

# 機器人類實踐

class robot
{
	private:
		// 機器人姓名
		char name[20];
		// 機器人型號
		char type[20];
	public:
		// 構造函數
		robot()
		{
			strcpy(name,"XXXXXX");
			strcpy(type,"XXXXXX");
		}
		// 設置修改數據
		void set(charn[],chart[])
		{
			strcpy(name,n);
			strcpy(type,t);
		}
		// 英文中每三位數讀法相同,所以定義 out 函數翻譯小於 1000 的整數
		void out(inta);
		// 將 1 至 1999999999 的整數翻譯成英文
		void tran_int(intn);
		// 析構函數
		~robot(){};
};
// 定義兩個全局字元指標陣列,存取所需的單字
//num1 中為 1 到 19,空出了 0,所以可以直接用 num1 [n] 調用,得到 n 對應單字
static char *num1[]=
	{
		"","one ","two ","three ","four ","five ","six ","seven ","eight ",
		"nine ","ten ","eleven ","twelve ", "thirteen ","fourteen ",
		"fifteen ","sixteen ","seventeen ","eighteen ","nineteen "
	};
//num10 中為 20-90,空出了 0 和 1,所以可以直接用 num10 [n/10] 調用,得到 n 對應單字
static char *num10[]=
	{
		"","","twenty ","thirty ","forty ","fifty ","sixty ","seventy ",
		"eighty ","ninety "
	};
// 小於 1000 整數翻譯函數
void robot::out(int a)
{
	int b=a%100;
	// 若百位不為零,輸出百位數加 hundred,
	// 若此時十位個位均為 0,不加 and
	if(a/100!=0)
	{
		cout<<num1[a/100]<<"hundred ";
		if(b!=0)
		{
			cout<<"and ";
		}
	}
	// 當後兩位在 20 以内時,直接調用 num1 [n],輸出
	if(b<20) {
		cout<<num1[b];
	}
	// 當後兩位大於 20 時
	else
	{
		// 先調用 num10,輸出十位数
		cout<<num10[b/10];
		// 個位不為 0 時應輸出 "-" 個位數
		if(b%10!=0)
		{
			cout<<"\b-"<<num1[b%10];
		}
	}
}
// 整數翻譯函數
void robot::tran_int(int n)
{
	if(n>1999999999)
	{
		cout<<"dev C++無法處理大於1999999999位的數!"<<endl;
	}
	else
	{
		// 三位三位取出,存入 abcd 中
		int a=n/1000000000,
			b=(n%1000000000)/1000000,
			c=(n%1000000)/1000,
			d=n%1000;
		/// 當 abcd 不等於 0 時,輸出並加上 billion, million 或 thousand
		if(a!=0)
		{
			out(a);
			cout<<"billion ";
		}
		if(b!=0)
		{
			out(b);
			cout<<"million ";
		}
		if(c!=0)
		{
			out(c);
			cout<<"thousand ";
		}
		if(d!=0)
		{
			// 據英文語法規則,最後兩位前一定有 and
			if(d<100&&(a!=0||b!=0||c!=0)) {
				cout<<"and ";
			}
			out(d);
		}
		cout<<endl;
	}
}
int main() {
	int n;
	cout<<"請輸入 n:";
	cin>>n;
	cout<<n<<endl;
	robot brown;
	brown.tran_int(n);
	return 0;
}

# 改良機器人類

class robot
{
	private:
		// 機器人姓名
		char name[20];
		// 機器人型号
		char type[20];
		// 帶翻譯的整數
		int num; 
		// 指向英文字元串
		char *ps; 
	public:
		// 構造函數
		robot()
		{
			strcpy(name,"XXXXXX");
			strcpy(type,"XXXXXX");
			num=0;
			ps=new char[5];
			strcpy(ps,"zero");
		}
		// 設置修改數據
		void set(charn[],chart[])
		{
			strcpy(name,n);
			strcpy(type,t);
		}
		// 英文中每三位數讀法相同,所以定義 out 函數翻譯小於 1000 的整數
		char *out(int a);
		// 將 1 至 1999999999 的整數翻譯成英文句子
		char *tran_int(int n);
		// 輸出整數及其英文句子
		void print_num();
		// 析構函數
		~robot(){
			// 釋放構造函數和 set 函數中動態申請的空間
			delete [] ps;// [] 表示 ps 指向的字元陣列,整個陣列都要被釋放
		};
};
// 定義兩個全局字元指標陣列,存取所需的單字
//num1 中為 1 到 19,空出了 0,所以可以直接用 num1 [n] 調用,得到 n 對應單字
static char *num1[]=
	{
		"","one ","two ","three ","four ","five ","six ","seven ","eight ",
		"nine ","ten ","eleven ","twelve ", "thirteen ","fourteen ",
		"fifteen ","sixteen ","seventeen ","eighteen ","nineteen "
	};
//num10 中為 20-90,空出了 0 和 1,所以可以直接用 num10 [n/10] 調用,得到 n 對應單字
static char *num10[]=
	{
		"","","twenty ","thirty ","forty ","fifty ","sixty ","seventy ",
		"eighty ","ninety "
	};
// 類外定義小於 1000 整數翻譯函數
char *robot::out(int a)
{
	// 定義字元陣列 k,長度設為 1000,用空字元串初始化,用來儲存英文字元串
	char k[1000]=""; 
	int b=a%100;
	// 若百位不為零,輸出百位數加 hundred,
	// 若此時十位個位均為 0,不加 and
	if(a/100!=0)
	{
		// 不直接輸出字元串,而是加入字元陣列 k 中
		strcat(k,num1[a/100]);
		strcat(k,"hundred ");
		if(b!=0) {
			strcat(k,"and ");
		}
	}
	// 當後兩位在 20 以內時,直接調用 num1 [n],輸出
	if(b<20) {
		strcat(k,num1[b]);
	}
	// 當後兩位大於 20 時
	else
	{
		// 先調用 num10,輸出十位數
		strcat(k,num10[b/10]);
		// 個位不為 0 時應输出 "-" 個位數
		if(b%10!=0)
		{
			strcat(k,"\b-");
			strcat(k,num1[b%10]);
		}
	}
	// 動態申請一個空間
	char *p=new char[strlen(k)+1];
	// 大小則根據 k 中實際長度 + 1
	// 調用完畢需要釋放該空間
	strcpy(p,k);
	return p;
}
// 類外定義整數翻譯函數
char *robot::tran_int(int n)
{
	char *p; // 定義字元指標 p
	char kk[1000]=""; // 定義字元陣列 kk,用來存放被翻譯出的英文字元串,初始化成空元串
	if(n>1999999999)
	{
		strcpy(kk,"dev C++無法處理太大的數!\n"); 
	}
	else
	{
		// 三位三位取出,存入 abcd 中
		int a=n/1000000000,
			b=(n%1000000000)/1000000,
			c=(n%1000000)/1000,
			d=n%1000;
		/// 當 abcd 不等於 0 時,輸出並加上 billion, million 或 thousand
		if(a!=0)
		{
			p=out(a);
			strcpy(kk,p);
			strcat(kk,"billion ");
			delete [] p; // 釋放在 out 函數中動態申請的空間
		}
		if(b!=0)
		{
			p=out(b);
			strcat(kk,p);
			strcat(kk,"million ");
			delete [] p; // 釋放在 out 函數中動態申請的空間
		}
		if(c!=0)
		{
			p=out(c);
			strcat(kk,p);
			strcat(kk,"thousand ");
			delete [] p; // 釋放在 out 函數中動態申請的空間
		}
		if(d!=0)
		{
			// 據英文語法規則,最後兩位前一定有 and
			if(d<100&&(a!=0||b!=0||c!=0)) {
				strcat(kk,"and ");
			}
			p=out(d);
			strcat(kk,p);
			delete [] p; // 釋放在 out 函數中動態申請的空間
		}
		p=new char[strlen(kk)+1];
		strcpy(p,kk);
		return p;
	}
}
// 類外定義設置函數
void robot::set(char n[],char t[],int m) // 設置修改數據
{
	strcpy(name,n);
	strcpy(type,t);
	if(num==m) {
		// 待翻譯的整數沒有變
		return;
	}
	else
	{
		num=m;
		delete [] ps; // 删除已有的英文句子
	}
	if(num>0) {
		char *tp=tran_int(num);
		ps=new char[strlen(tp)+1];
		strcpy(ps,tp);
		delete [] tp; // 釋放在 trans_int 中動態申請空間
	}
	else if(num==0)
	{
		ps=new char[5];
		strcpy(ps,"zero");
	}
	else
	{
		ps=new char[13];
		strcpy(ps, "負數不能翻譯");
	}
}
int main() {
	robot brown;
	brown.print_num();
	int n;
	cout<<"請輸入n:";
	cin>>n;
	brown.set("brown","800#",n);
	brown.print_num();
	return 0;
}

# 對象與指標

class Person
{
	private:
		char Name[9];
		char Sex;
		int Age;
		char Pid[19];
		char *Addr;
	public:
		Person()
		{
			strcpy(Name,"xxxxxxxx");
			Age = 0;
			Sex = 'x';
			strcpy(Pid,"xxxxxxxxxxxxxxxxxx");
			Addr=NULL;
		}
		Person(char *N, int A, char S,char *P,char *Ad)
		{
			strcpy(Name,N);
			Age = A;
			Sex = S;
			strcpy(Pid,P);
			int L=strlen(Ad);
			Addr=new char[L+1];
			strcpy(Addr,Ad);
		}
		~Person()
		{
			cout<<"Now destroying Person"<<Name<<endl;
			delete [] Addr;
		}
		void Register( char *name, int age, char sex,char *pid,char *addr);
		void ShowMe();
};
void Person::Register(char *name,int age,char sex,char *pid,char *addr)
{
	strcpy(Name,name);
	Age = age;
	Sex = sex;
	strcpy(Pid,pid);
	if(Addr!=NULL) {
		delete [] Addr;
	}
	int L=strlen(addr);
	Addr=new char[L+1];
	strcpy(Addr,addr);
}
void Person::ShowMe()
{
	cout<<Name<<" "<<Age<<" "<<Sex<<" "<<Pid<<" ";
	if(Addr!=NULL) {
		cout<<Addr;
	}
	cout<<endl;
}
int main()
{
	Person person1;
	Person person2("張三",19,'m',"610103199409192839","嘉義縣");
	Person person3;
	cout<<"person1: ";
	person1.ShowMe();
	cout<<"person2: ";
	person2.ShowMe();
	person3.Register("李四",29,'w',"610103198409192493","台北市");
	cout<<"person3: ";
	person3.ShowMe();
	person2.Register("趙五",29,'w',"610103198409152127","台南市");
	cout<<"person2: ";
	person2.ShowMe();
	return 0;
}

# 指向對象的指標

<類名> *<指標變數表>;
// 例子
Person person1("Zrn",19,'f');
Person *ptr=&person1;
ptr->ShowMe();

# 動態存儲

<對象指標>new <類名>(<名字初始化值>);
delete <名字指標>;
// 例子
Person *p=new Person;
delete p;

# 修改版日期類

#include<iostream>
using namespace std;
class Date
{
	public:
		int year,month,day;
		void init(int y,int m,int k);
		void print_ymd();
};
void Date::init(int yy,int mm,int dd)
{
	month = mm;
	year = yy;
	day = dd;
}
void Date::print_ymd()
{
	cout<<year<<"-"<<month<<"-"<<day<<endl;
}
int main()
{
	Date date1;
	Date *p1 = &date1;
	p1->init(2006,5,13);
	p1->print_ymd();
	int *p2 = &date1.year;
	cout << *p2 <<endl;
	void (Date::*p3)(int ,int ,int); // 普通函數指標定義不行
	void (Date::*p4)();
	p3 = Date::init;
	p4 = Date::print_ymd;
	(date1.*p3)(2006,4,8);
	(date1.*p4)();
	return 0;
}

# 修改 Person 類

#include<iostream>
#include<string.h>
using namespace std;
class Person
{
	private:
		char Name[9];
		char Sex;
		int Age;
	public:
	Person()
	{
		strcpy(Name,"XXX");
		Age = 0;
		Sex = ' ';
	}
	~Person()
	{
		cout<<"Now destroying Person"<<endl;
	}
	void Register( char *name, int age, char sex);
	void ShowMe();
};
void Person::Register(char *name,int age,char sex)
{
	strcpy(Name,name);
	Age = age;
	Sex = sex;
}
void Person::ShowMe()
{
	cout<<Name<<'\t'<<Age<<'\t'<<Sex<<endl;
}
int main()
{
	Person *p1,*p2; // 宣告兩個指向對象的指標
	p1=new Person; // 動態生成一個 Person 對象
	cout << "person1: \t";
	p1->ShowMe();
	p1->Register("Zhang3", 19, 'm');
	cout << "person1: \t";
	p1->ShowMe();
	p2=new Person; // 動態生成一個 Person 對象
	cout << "person2: \t";
	p2->ShowMe();
	*p2 = *p1; // 對象之間的賦值
	cout << "person2: \t";
	p2->ShowMe();
	delete p1; // 釋放 p1 指標指向對象所占的空間
	delete p2; // 释放 p2 指標指向對象所占的空間
	return 0;
}

# this 指標

  • 每一個類的成員函數都包含一個指向本類對象的指針,指針名為 this
  • 該指針指向本類對象的起始地址
    this 是指針, *this 就是對象(本對象)
  • 訪問成員
    this->成員(*this).成員
    . 運算符優先級比較高,故需要使用 ()
#include <iostream>
using namespace std;
class Test
{
	int x;
	public:
		Test( int = 0 );
		void print();
};
// 構造函數
Test::Test(int a)
{
	x = a;
}
void Test::print()
{
	cout << " x = " << x <<endl;
	cout << "this->x = " << this->x << endl;
	cout << "(*this).x = " << ( *this ).x << endl;
}
int main()
{
	Test testObject( 12 );
	testObject.print();
	return 0;
}
  • 常用場合
  1. 當類中數據成員名與成員函數中的形參名相同時,用 this 指針加以區分
class Time
{
	private:
		int hour,minute,second;
	public:
		void set(int hour,int minute,int second)
		{
			this->hour = hour;
			this->minute = minute;
			this->second = second;
		}
}
// 兩個整數為參數的構造函數
Fraction::Fraction(int a,int b)
{
	set(a,b); // 調用函式成員
}
// 設置分子、分母
void Fraction::set(int a,int b)
{
	this->a=a;
	this->b=b;
}
// 分數相加,本類對象加 u
Fraction Fraction::add(Fraction u)
{
	int tmp;
	Fraction v;
	v.a=a*u.b+b*u.a; // 分子
	v.b=b*u.b;// 分母
	tmp=divisor(v.a ,v.b);// 計算分子、分母公因數
	v.a=v.a/tmp;// 約去公因數
	v.b=v.b/tmp;// 約去公因數
	return v; // 返回结果
}
  1. 成員函數需要返回指針或本對象時
// 分數相加,本類對象加 u 
Fraction Fraction::add(Fraction u) { 
    int tmp; 
    // Fraction v; 
    a=a*u.b+b*u.a; // 分子 
    b=b*u.b;// 分母 
    tmp=divisor(a ,b);// 計算分子、分母的公因數 
    a=a/tmp;// 約去公因數 
    b=b/tmp;// 約去公因數 
    return *this; // 返回結果 
}
更新於 閱讀次數

用實際行動犒賞爆肝的我😀

Zrn Ye LinePay

LinePay