【C++ 05】類和對象-對象的初始化和清理

1 構造函數和折構函數

對象的初始化和清理是兩個非常重要的安全問題

一個對象或者變量沒有初始狀態,對其使用後果是未知的;

同樣的,使用完一個對象和變量,沒有及時清理,也會造成一定的安全問題;

C++利用了構造函數和析構函數,解決上述問題;這兩個函數將會被編譯器自動錶用,完成對象初始化和清理工作。對象的初始化和清理工作是編譯器強制我們做的事情,因此如果我們不提供析構函數,編譯器會執行編譯器提供的構造函數和析構函數(空實現)

構造函數:主要作用是創造對象時爲對象的成員屬性賦值,構造函數由編譯器自動調用,無需手動調用。

析構函數:主要作用於對象銷燬前,系統自動調用,執行一些清理工作。

構造函數語法:類名(){}

  1. 1.構造函數,沒有返回值,也不寫void
  2. 2.函數名稱與類名相同
  3. 3.構造函數可以有參數,因此可以發生重載
  4. 4.程序在調用對象會自動調用構造,無需手動調用,而且只會調用一次;

析構函數:~類名(){}

  1. 1.析構函數,沒有返回值也不寫void
  2. 2.函數名稱和類名相同,在名稱前加上符號~
  3. 3.析構函數不可以有參數,因此不可以發生重載;
  4. 4.程序在對象銷燬前會自動調用析構,無需手動調用,而且只會調用一次;
//1.構造函數
class Person01
{
public:
	Person01()
	{
		cout << "構造函數的調用" << endl;
	}
	//2.析構函數
	//析構函數不可以有參數,不可以發生重載
	//在銷燬前會自動的調用析構函數,且只調用一次
	~Person01()
	{
	  cout << "析構函數的調用" << endl;
	} 
};

void test01()
{
	Person01 p;
}
int main()
{
	//test01();
	system("pause");
	return 0;
}

2 構造函數的分類和調用

兩種分類方式:

按參數分爲:有參構造、無參構造;

按照調用分爲: 括號法、顯示法、隱式函數轉換法

注意事項1 調用構造默認函數時候,不要+();

注意事項2: 不用利用拷貝構造函數初始化匿名對象

class Person01
{
public:
	Person01()
	{
		cout << "Person01 無參構造函數的調用" << endl;
	}
	Person01(int a)
	{
		age = a;
		cout << "Person01 有參構造函數的調用" << endl;
	}
	//拷貝構造函數
	Person01(const Person01 &p)
	{
		//將傳入的人身上的所有參數都copy過來
		age = p.age;
		cout << "Person01copy 構造函數的調用" << endl;
	}
	//2.析構函數

	~Person01()
	{
	  cout << "Person01 析構函數的調用" << endl;
	} 
	int age;
};

void test01()
{
	// 1 括號法
	//Person01 p; //無參函數構造
	//Person01 p2(10);//有參函數構造;
	//拷貝構造函數
	//Person01 p3(p2);
	/*cout << "p2 的年齡 = " << p2.age << endl;
	cout << "p3 的年齡 = " << p3.age << endl;*/
	//注意事項1 調用構造默認函數時候,不要+();
	//因爲下面這行代碼,編譯器會認爲是一個函數的聲明,不會認爲在創建對象
	//Person01 p1(); 

	//2、顯示法
	//注意事項二: 不用利用拷貝構造函數初始化匿名對象
	Person01 p1;
	Person01 p2 = Person01(10); //有參構造
	Person01 p3 = Person01(p2); //拷貝構造

	//Person01(p3);//編譯器會認爲Person01(p3) = Person01 p3;對象重定義

	//Person01(10);//匿名對象 特點,當前行執行結束之後,系統會立即回收匿名對象;

	//隱式轉換法
	Person01 p4 = 10; // Person01 p4(10) || Person01 p4 = Person01(10);

}
int main()
{
	test01();

	system("pause");
	return 0;
}

3 拷貝構造函數調用時機

C++中拷貝構造函數調用時機通常有三種

  • 使用一個以及創建完成的對象來初始化一個對象
  • 值傳遞的方式給函數參數傳遞
  • 以值方式返回局部對象
//1.使用一個已經創建完畢的對象來初始化一個新對象
void test02()
{
	Person02 p1(20);
	Person02 p2(p1);

}

//2.值傳遞的方式給函數參數傳值
void doWork(Person02 p)
{

}
void test03()
{
	Person02 p;
	doWork(p);
}
//3.值方式返回局部變量
Person02 doWork01()
{
	Person02 p1;
	cout << (int*)& p1 << endl;
	return p1;
}

void test04()
{
	Person02 p = doWork01();
	cout << (int*)& p << endl;
}
int main()
{
	test04();

	system("pause");
	return 0;
}

4 構造函數調用的規則

默認情況下,C++編譯器至少給一個類添加三個函數

  1. 默認構造函數(空實現)
  2. 析構函數(空實現)
  3. 拷貝構造函數(值拷貝)

構造函數調用規則:

  • 如果用戶定義了有參構造函數,C++不會提供無參構造函數,但是會提供拷貝構造函數
  • 如果用戶定義了拷貝構造函數,C++不會再提供其他構造函數;

5 深拷貝與淺拷貝

淺拷貝:簡單的賦值拷貝操作 ;淺拷貝帶來的問題是堆區內存的重複釋放;

深拷貝:在堆區重新申請空間進行拷貝操作;

class Person02
{
public:
	Person02()
	{
		cout << "Person 無參構造函數" << endl;
	}
	Person02(int age ,int  height)
	{
		m_Age = age;
		m_Hight = new int(height);
		cout << "Person 有參構造函數" << endl;
	}
	Person02(const Person02 &a)
	{
		m_Age = a.m_Age;
		m_Hight = new int(*a.m_Hight);
		cout << "Person 拷貝參構造函數" << endl;
	}
	~Person02()
	{
		if (m_Hight != NULL)
		{
			delete m_Hight;
		}
		cout << "Person02析構函數的調用" << endl;
	}
	int m_Age;
	int* m_Hight;
};


void test05()
{
	Person02 p1(18,160);
	cout << "p1.age =" << p1.m_Age << endl;
	cout << "p1.m_Height =" <<  *p1.m_Hight << endl;
	Person02 p2(p1);
	cout << "p2.age =" << p2.m_Age << endl;
	cout << "p2.m_Height =" << *p2.m_Hight << endl;
}


int main()
{
	test05();

	system("pause");
	return 0;
}

總結:如果屬性有在堆區開闢的內存,一定要自己提供拷貝構造函數,防止淺拷貝帶來的問題;

6 初始化列表

作用:

C++提供了初始化列表語法,用來初始化屬性;

語法: 構造函數():屬性1(值),屬性2(值)...{}

class Person03 
{
public:
	//傳統賦值操作
	//Person03(int a, int b, int c)
	//{
	//	m_A = a;
	//	m_B = b;
	//	m_C = c;
	//}

	//初始化列表
	Person03(int a,int b,int c) : m_A(a), m_B(b), m_C(c) {

	}
	int m_A;
	int m_B;
	int m_C;
};

void test06()
{
	Person03 p(30,20,10);
	cout << "m_A 的值" << p.m_A << endl;
	cout << "m_B 的值" << p.m_B << endl;
	cout << "m_C 的值" << p.m_C << endl;
}
int main()
{
	test06();
	system("pause");
	return 0;
}

7 類對象作爲類成員

C++類中的成員可以是另一個類的對象,我們稱之爲對象成員;

例如B類中有 對象A 作爲成員,A爲對象成員;那麼當創建B對象時,A與B的構造和析構的順序是誰先誰後呢?

先構造A後構造B;

當其他類的對象作爲本類的成員,當構造的時候需要先構造成員類對象,再構造類本身;析構的順序與之相反;、

//類對象作爲類成員
class Phone 
{
public:
	Phone(string name)
	{
		cout << "Phone 構造函數" << endl;
		m_PName = name;
	}
	string m_PName;
};
class Person04
{
public:
	Person04(string name, string pName) : m_Name(name),m_Phone(pName)
	{
		cout << " Person04 構造函數" << endl;
	}
	string m_Name;
	Phone m_Phone;
};

int main()
{
	
	Person04 p("zhangsan ", "iphone");
	cout << p.m_Name << " 拿着 "  <<  p.m_Phone.m_PName << endl;
	system("pause");
	return 0;
}

8 靜態成員函數

靜態成員就是在成員變量和成員函數前加上關鍵字static ,稱之爲靜態成員;

靜態成員分爲:

  • 靜態成員變量
  1.            所有對象共享一份數據
  2.            在編譯階段分配內存
  3.           類內聲明,類外初始化
  • 靜態成員函數
  1.                所有對象共享一個函數
  2.                靜態成員函數只能訪問靜態成員變量
//靜態成員函數
//所有對象共享一個函數
//靜態成員函數只能訪問靜態成員變量

class person
{
public:
	static void func()
	{
		m_A = 100;//靜態成員函數無法訪問非靜態成員變量,因爲無法區別那個對象的變量
		cout << "static void func " << endl;
	}

	static int m_A;
	int m_B;
	//靜態成員函數也是有訪問權限的
private:
	static void func2()
	{

	}
};
//兩種方式
void test07()
{
	//通過對象訪問
	person p;
	p.func();
	//通過類名訪問,不需要對象
	person::func();


}
int main()
{
	test07();

	system("pause");
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章