1. 程式人生 > >c++3------類,面向物件的三大特性

c++3------類,面向物件的三大特性

一、c++中的類即可用class也可用struct

(1)類中的變數成為類中的屬性或成員變數;

(2)類中的函式成為方法或類中的行為或成員函式;

1、類的定義:

法1:類的宣告和定義全部放在類體中

class className
{
// 類體:由函式和變數組成
}; // 一定要注意後面的分號
class為定義類的關鍵字,ClassName為類的名字,{}中為類的主體,注意類定義結束時後面分號。類中的元素稱為類的成員;類中的資料稱為類的屬性或者類的成員變數;類中的函式稱為類的方法或者類的成員函式

法2:把類的宣告和定義分開 :類的宣告放在.h檔案中,類的定義放在.cpp檔案中:類裡面的成員放到類外進行定義,需要加上類名::

.h

#pragma once
struct student
{
	void SetInfo(char *name, int age, char *gender);
	void PrintStudent();
	char _name[20];
	int _age;
	char _gender[3];
};

.c

# include"student.h"
void student::SetInfo(char *name, int age, char *gender)
{

}
void student::PrintStudent()
{

}

(1)防止標頭檔案被重複多次引用的方式:1'#pragma once     2'#ifndef __標頭檔案名__     #define  __標頭檔案名__

   #endif

這兩種方式的區別為:

2、類的作用域:

類定義了一個新的作用域,類的所有成員都必須處在類的作用域中。形參表和函式體處於類的作用域中。在類體外定義成員,需要使用 :: 作用域解析符指明成員屬於哪個類域。在類的作用域外,只能夠通過物件(或指標)藉助成員訪問操作符.和->來訪問類成員,跟在訪問操作符後面的名字必須在相關聯類的作用域中。

namespace N
{
	int a = 10;
}
int a = 20;
class Test
{
public:
	void FunTest(int a)
	{
		a = a;
	}
	void PrintA()
	{
		cout << a << endl;
	}
private:
	int a;
};
int main()
 {
	int a = 40;
	Test t;
	t.FunTest(30);
 	cout << N::a << endl;//列印名稱空間裡的
	cout << a << endl;//函式裡面沒有就去全域性作用域查詢,若是全域性作用域也沒有
t.PrintA();//列印隨機值,因為引數和返回值相同,不確定是哪個給哪個賦值 return 0; }

函式裡面有a就去函式裡面找, 第三個列印隨機值,解決方法:給成員變數的名字前面加上下劃線加以區分。

注意:以下程式中,此時兩個FunTest函式沒有在同一作用域中,不構成過載。

void FunTest()
{
}
class Test
{
public:
	void FunTest(int a)
	{
		a = a;
	}
}

3、類的例項化

用類型別建立物件的過程,稱為類的例項化

(1. 類只是一個模型一樣的東西,限定了類有哪些成員,定義出一個類並沒有分配實際的記憶體空間來儲存它
(2. 一個類可以例項化出多個物件,例項化出的物件佔用實際的物理空間儲存類成員變數
(3. 做個比方。類例項化出物件就像現實中使用建築設計圖建造出房子,類就像是設計圖,只設計出需要什麼東西,但是並沒有實體的建築存在,同樣類也只是一個設計,例項化出的物件才能實際儲存資料,佔用物理空間

4、類的物件模型:類中各成員在記憶體中的佈局形式

什麼是類?什麼是類物件?類物件中包含哪些成員?計算一個類的大小。

類例項化為物件

儲存方式一:

缺陷:每個物件中成員變數是不同的,但是函式都是相同的,如果一個類建立多個物件,每個物件中都會儲存一份程式碼,相同程式碼儲存多次,浪費空間。沒有采用方式一。

儲存方式二:多給一個指標,存放成員函式表的首地址


方式二明顯比方式一節省了太多的空間,但物件中還是多了一個指標。採用這種方式,最終的大小比實際大小多一個指標的的大小,但是VS編譯後最終的大小沒有那個指標的大小(只有成員變數的大小之和,記憶體對齊),所以沒有采用這種方式。沒有采用方式二。

儲存方式3:沒有儲存指標,成員變數與成員函式分開,只用儲存成員變數,不用管成員函式,將成員函式單獨儲存,編譯器編譯結束後,記得函式的入口地址,找到該函式。當沒有成員變數,只有成員函式(空類)時,大小預設為1(在g++和VS編譯環境下)。空類大小為什麼是1?定義三個不同的物件,若是空類的大小為0,則這三個物件都在地址0處,與三個不同的物件衝突。)採用了方式三。

問題1:函式只有一份,通過不同的物件來呼叫函式,函式體的裡面每一個成員變數我們並不知道是屬於哪一個物件,但是函式呼叫完畢,可以正確的把引數的資訊設定到某一個物件上去,是怎麼實現的?(C語言中的結構體不能定義函式)

問題2:成員變數在成員函式的後面,成員函式仍然可以正確的呼叫

答:(問題2的答案與問題1的大同小異)this指標。看起來成員變數前面什麼都沒有,實際上,在成員變數的前面存在一個this指標,誰呼叫這個函式this指標指向誰。this指標是編譯器自動加上的,不用程式設計師手工新增。this指標通過ecx暫存器傳遞。this指標指向當前物件,不能作為左值,所以this不會被賦空。 編譯器編譯時會對程式碼進行修改:

1、識別類名
2、識別類中的成員變數
3、識別成員函式並對成員函式進行修改:先該函式原型,再改函式體:先修該函式的引數列表this指標指向類物件
(當前呼叫這個函式的物件)
struct student
{
public:
	void SetInfo(char *name, int age, char *gender)
	{

 		strcpy(_name, name);
		_age = age;
		strcpy(_gender, gender);
	}
	/*void SetInfo(student *this, char *name, int age, char *gender)
	{
		strcpy(this->_name, name);
		this->_age = age;
		strcpy(this->_gender, gender);
	}*/
	void PrintStudent()
	{
		cout << _name << " " << _age << " " << _gender << endl;
	}
	/*void PrintStudent(student *this)
	{
		cout << this->_name << " " << this->_age << " " << this->_gender << endl;
	}*/
private:
	int _a;
	char _name[20];
	int _age;
	char _gender[3];
};

 二、面向物件(C++)的三大特性:(1)封裝:函式是封裝的一種形式:函式中的語句被封裝在函式本身這個更大的實體,被封裝的實體隱藏了它們的實現細節,可以呼叫該函式但是不能夠訪問函式中的語句

封裝:隱藏物件的屬性和實現細節,僅對外公開介面和物件進行互動,將資料和操作資料的方法進行有機結合。如:函式(將多條語句封裝到一塊)

1’. public成員在類外可以直接訪問
2'. protected和private成員在類外(在此可將protected和private理解成private)不能夠訪問
3'. 它們的作用域從該訪問限定符出現的位置開始直到下一個訪問限定符出現時為止
4'. class的預設訪問許可權是private,而struct為public型(因為struct要相容C)

類內:成員在類的花括號裡面;

類外:成員在類的花括號外面

訪問限定符只在編譯時有用,當資料對映到記憶體後,沒有任何訪問限定符上的區別

思考題:如何在類外訪問一個類中私有的成員變數?

1’、新增公有的方法來在類外訪問一個類中私有的成員變數。(一般給成員變數前面加上下劃線,用此來區分成員變數和外部的變數。

struct student
{
public:
	void SetInfo(char *name, int age, char *gender)
	{
		strcpy(_name, name);
		_age = age;
		strcpy(_gender, gender);
	}
	void PrintStudent()
	{
		cout << _name << " " << _age << " " << _gender << endl;
	}
	void SetA(int a)
	{
		_a = a;
	}
	int GetA()
	{
		return _a;
	}
private:
	int _a;
	char _name[20];
	int _age;
	char _gender[3];
};
int main()
{
	int a;
	student s1, s2;
	s1.SetA(10);
	s1.GetA();
	s1.SetInfo("將舊", 28, "女");
	s2.SetInfo("卡卡", 3, "男");
	s1.PrintStudent();
	s2.PrintStudent();
	return 0;
}

2‘、友元函式

3'、通過指標操作類的成員變數,必須知道類的結構(類如何佈局),知道類的型別,這樣才知道按照什麼方式進行解析。(char *)((int)&s1+offsetof(student,_name))

struct student
{
public:
	void SetInfo(char *name, int age, char *gender)
	{
		strcpy(_name, name);
		_age = age;
		strcpy(_gender, gender);
	}
	void PrintStudent()
	{
		cout << _name << " " << _age << " " << _gender << endl;
	}
private:
	int _a;
	char _name[20];
	int _age;
	char _gender[3];
};
int main()
{
	int a;
	student s1, s2;
	int *pa = (int *)&s1;
	*pa = 100;
	s1.SetInfo("將舊", 28, "女");
	s2.SetInfo("卡卡", 3, "男");
	s1.PrintStudent();
	s2.PrintStudent();
	return 0;
}

(2)繼承:

(3)多型: