成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

C++ 類與對象(上)

alanoddsoff / 1773人閱讀

摘要:中默認的對齊數(shù)為結(jié)構(gòu)體總大小為最大對齊數(shù)所有變量類型最大者與默認對齊參數(shù)取最小的整數(shù)倍。性能原因數(shù)據(jù)結(jié)構(gòu)尤其是棧應該盡可能地在自然邊界上對齊。

類的引入

C語言中,結(jié)構(gòu)體中只能定義變量,在C++中,結(jié)構(gòu)體內(nèi)不僅可以定義變量,也可以定義函數(shù)。

struct Student{	//在類里面定義函數(shù)	void SetStudentInfo(const char* name, const char* gender, int age)	{		strcpy(_name, name);		strcpy(_gender, gender);		_age = age;	}		//在類里面定義函數(shù)	void PrintStudentInfo()	{		cout<<_name<<" "<<_gender<<" "<<_age<<endl;	}    //在類里面定義變量char _name[20];char _gender[3];int _age;};int main(){	Student s;	s.SetStudentInfo("Peter", "男", 18);	return 0;}

上面結(jié)構(gòu)體的定義,在C++中更喜歡用class來代替

類的定義

class className{// 類體:由成員函數(shù)和成員變量組成}; // 一定要注意后面的分號

class為定義類的關鍵字,ClassName為類的名字,{}中為類的主體,注意類定義結(jié)束時后面分號。

類中的元素稱為類的成員:類中的數(shù)據(jù)稱為類的屬性或者成員變量; 類中的函數(shù)稱為類的方法或者成員函數(shù)。

類的兩種定義方式:

  1. 聲明和定義全部放在類體中,需要注意:成員函數(shù)如果在類中定義,編譯器可能會將其當成內(nèi)聯(lián)函數(shù)處

  2. 聲明放在.h文件中,類的定義放在.cpp文件中

一般情況下,更期望采用第二種方式。

類的訪問限定符及封裝

訪問限定符

C++實現(xiàn)封裝的方式:用類將對象的屬性與方法結(jié)合在一塊,讓對象更加完善,通過訪問權限選擇性的將其
接口提供給外部的用戶使用

【訪問限定符說明】

  1. public修飾的成員在類外可以直接被訪問

  2. protected和private修飾的成員在類外不能直接被訪問(此處protected和private是類似的)

  3. 訪問權限作用域從該訪問限定符出現(xiàn)的位置開始直到下一個訪問限定符出現(xiàn)時為止

  4. class的默認訪問權限為private,struct為public(因為struct要兼容C)
    注意:訪問限定符只在編譯時有用,當數(shù)據(jù)映射到內(nèi)存后,沒有任何訪問限定符上的區(qū)別

【面試題】
問題:C++中struct和class的區(qū)別是什么?
解答:C++需要兼容C語言,所以C++中struct可以當成結(jié)構(gòu)體去使用。另外C++中struct還可以用來定義類。
和class是定義類是一樣的,區(qū)別是struct的成員默認訪問方式是public,class是struct的成員默認訪問方式
是private。

封裝

【面試題】 面向?qū)ο蟮娜筇匦裕悍庋b、繼承、多態(tài)。
在類和對象階段,我們只研究類的封裝特性,那什么是封裝呢?
封裝:將數(shù)據(jù)和操作數(shù)據(jù)的方法進行有機結(jié)合,隱藏對象的屬性和實現(xiàn)細節(jié),僅對外公開接口來和對象進行交互。
封裝本質(zhì)上是一種管理:我們?nèi)绾喂芾肀R俑呢?比如如果什么都不管,兵馬俑就被隨意破壞了。那么我們首先建了一座房子把兵馬俑給封裝起來。但是我們目的全封裝起來,不讓別人看。所以我們開放了售票通
道,可以買票突破封裝在合理的監(jiān)管機制下進去參觀。類也是一樣,我們使類數(shù)據(jù)和方法都封裝到一下。
不想給別人看到的,我們使用protected/private把成員封裝起來。開放一些共有的成員函數(shù)對成員合理的訪問。所以封裝本質(zhì)是一種管理

類的作用域

類定義了一個新的作用域,類的所有成員都在類的作用域中。在類體外定義成員,需要使用 :: 作用域解析符指明成員屬于哪個類域

class Person{public:	void PrintPersonInfo();private:char _name[20];char _gender[3];int _age;};// 這里需要指定PrintPersonInfo是屬于Person這個類域void Person::PrintPersonInfo(){	cout<<_name<<" "_gender<<" "<<_age<<endl;}

短小的的成員函數(shù),直接在類里面定義,就默認是內(nèi)聯(lián)函數(shù)

代碼長的成員函數(shù)建議聲明和定義分離

私有的數(shù)據(jù)可以通過共有的函數(shù)來操作,比如棧,棧的成員變量可以私有,在外部就無法訪問成員變量,要想對成員變量進行操作可以使用公有的成員函數(shù)。

類的實例化

  1. 類只是一個模型一樣的東西,限定了類有哪些成員,定義出一個類并沒有分配實際的內(nèi)存空間來存儲它
  2. 一個類可以實例化出多個對象,實例化出的對象 占用實際的物理空間,存儲類成員變量
  3. 做個比方。類實例化出對象就像現(xiàn)實中使用建筑設計圖建造出房子,類就像是設計圖,只設計出需要什么東西,但是并沒有實體的建筑存在,同樣類也只是一個設計,實例化出的對象才能實際存儲數(shù)據(jù),占用物理空間

類對象模型

如何計算類對象的大小

class A{public:void PrintA(){	cout<<_a<<endl;}    private:char _a;};

問題:類中既可以有成員變量,又可以有成員函數(shù),那么一個類的對象中包含了什么?如何計算一個類的大???

答案:一個類的對象中只包含了成員變量,一個類的大小只包括成員變量的大小,根據(jù)內(nèi)存對齊方式來確定類的大小。

類對象的存儲方式猜測

  • 對象中包含類的各個成員

    缺陷:每個對象中成員變量是不同的,但是調(diào)用同一份函數(shù),如果按照此種方式存儲,當一個類創(chuàng)建多個對象時,每個對象中都會保存一份代碼,相同代碼保存多次,浪費空間。那么如何解決呢

  • 只保存成員變量,成員函數(shù)存放在公共的代碼段

一個類實例化出N個對象,每個對象的成員變量可以存儲不同的值,但是調(diào)用的函數(shù)卻是同一個,如果每個對象都放成員函數(shù),而這些成員函數(shù)卻是一樣的,就會浪費空間。

我們再通過對下面的不同對象分別獲取大小來分析看下

// 類中既有成員變量,又有成員函數(shù)class A1 {public:void f1(){}private:int _a;};// 類中僅有成員函數(shù)class A2 {public:void f2() {}};// 類中什么都沒有---空類class A3{};

sizeof(A1) : 4

sizeof(A2) : 1

sizeof(A3) : 1

結(jié)論:一個類的大小,實際就是該類中”成員變量”之和,當然也要進行內(nèi)存對齊,注意空類的大小,空類比
較特殊,編譯器給了空類1個字節(jié)來唯一標識這個類。

為什么給了1個字節(jié)而不是0個字節(jié)呢?

開1個字節(jié)不是為了存數(shù)據(jù),是為了占位,表示其存在。

如果一個類沒有成員,那么他的對象需要給1byte進行占位來標識對象存在,這1byte不存儲有效數(shù)據(jù)

結(jié)構(gòu)體內(nèi)存對齊規(guī)則

  1. 第一個成員在與結(jié)構(gòu)體偏移量為0的地址處。

  2. 其他成員變量要對齊到某個數(shù)字(對齊數(shù))的整數(shù)倍的地址處

    注意:對齊數(shù) = 編譯器默認的一個對齊數(shù) 與 該成員大小的較小值。
    VS中默認的對齊數(shù)為8

  3. 結(jié)構(gòu)體總大小為:最大對齊數(shù)(所有變量類型最大者與默認對齊參數(shù)取最?。┑恼麛?shù)倍。

  4. 如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對齊到自己的最大對齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是
    所有最大對齊數(shù)(含嵌套結(jié)構(gòu)體的對齊數(shù))的整數(shù)倍。

【面試題】

  1. 結(jié)構(gòu)體怎么對齊? 為什么要進行內(nèi)存對齊

    1. 平臺原因(移植原因): 不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。

    2. 性能原因: 數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應該盡可能地在自然邊界上對齊。 原因在于,為了訪問未對齊的內(nèi)存,處理需要作兩次內(nèi)存訪問;而對齊的內(nèi)存訪問僅需要一次訪問

  2. 如何讓結(jié)構(gòu)體按照指定的對齊參數(shù)進行對齊

    使用#pragma pack

  3. 如何知道結(jié)構(gòu)體中某個成員相對于結(jié)構(gòu)體起始位置的偏移量

    C語言中的宏offsetof可以計算,本質(zhì)上是:#define my_offsetof(s, m) (size_t)&(((s*)(0))->m)

    (s*)(0)意思是將0地址變成一個s類型的結(jié)構(gòu)體的起始地址,再對引用某個成員(s *)(0)->m,對該成員取地址

    &((s *)(0)->m),得到的是該成員的地址,而因為起始地址為0,所以該成員的地址就是相對起始位置的偏移量。

  4. 什么是大小端?如何測試某臺機器是大端還是小端,有沒有遇到過要考慮大小端的場景

    大小端在計算機業(yè)界,Endian表示數(shù)據(jù)在存儲器中的存放順序

    大端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高地址中,這樣的存儲模式有點兒類似于把數(shù)據(jù)當作字符串順序處理:地址由小向大增加,而數(shù)據(jù)從高位往低位放;

    小端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的高地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的低地址中,這種存儲模式將地址的高低和數(shù)據(jù)位權有效地結(jié)合起來,高地址部分權值高,低地址部分權值低,和我們的邏輯方法一致。

    比如一個整型10,將它的32位按十六進制從高位到低位表示:00 00 00 0A

    在大端中的存儲方式是:

    小端中的存儲方式是:

    測試大小端(使用共用體Union):

    union U {	char a;	int b;};int main(){	U u1;	u1.b = 1;	if (u1.a == 1)	{		printf("小端/n");	}	else		printf("大端/n");	return 0;}

    原理:U中的a和b是共用一塊空間的,a占一個字節(jié),能使用的只有b的一個低地址處的字節(jié)

    如果當前機器是小端,則b=1,按照地址從低到高,存儲方式就是:01 00 00 00

    a能夠訪問的就是低地址處的01

    如果當前機器是大端,則b=1,按照地址從低到高,存儲方式就是:0 00 00 01

    a能夠訪問的就是低地址處的00

this指針

this指針的引出

我們先來定義一個日期類Date

class Date{public :void Print (){cout <<_year<< "-" <<_month << "-"<< _day <<endl;}    void SetDate(int year , int month , int day){_year = year;_month = month;_day = day;}    private :int _year ; // 年int _month ; // 月int _day ; // 日};int main(){    Date d1, d2;    d1.SetDate(2018,5,1);    d2.SetDate(2018,7,1);        d1.Print();    d2.Print();    return 0;}

對于上述類,有這樣的一個問題:

Date類中有SetDate與Print兩個成員函數(shù),函數(shù)體中沒有關于不同對象的區(qū)分,那當s1調(diào)用SetDate函數(shù)
時,該函數(shù)是如何知道應該設置s1對象,而不是設置s2對象呢?

C++中通過引入this指針解決該問題,即:C++編譯器給每個“非靜態(tài)的成員函數(shù)“增加了一個隱藏的指針參
數(shù),讓該指針指向當前對象(函數(shù)運行時調(diào)用該函數(shù)的對象),在函數(shù)體中所有成員變量的操作,都是通過該
指針去訪問。只不過所有的操作對用戶是透明的,即用戶不需要來傳遞,編譯器自動完成。

所以上述函數(shù)調(diào)用時,實際上是這樣的:

//規(guī)定this指針默認放在第一個參數(shù)位置d1.SetDate(&d1,2018,5,1);d2.SetDate(&d2,2018,7,1);d1.Print(&d1);d2.Print(&d2);

但我們調(diào)用時不得私自加上這個指針參數(shù)

并且在定義類成員函數(shù)時,實際上也是這樣的

//在定義成員函數(shù)時也可以這么寫void Display()	{		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;	}void SetDate(int year, int month, int day)	{		this->_year = year;		this->_month = month;		this->_day = day;	}

我們只是看上去沒有傳參數(shù), 實際上是傳了調(diào)用Print函數(shù)的類的地址。

this指針的特性

  1. this指針的類型:類類型* const
  2. 只能在“成員函數(shù)”的內(nèi)部使用
  3. this指針本質(zhì)上其實是一個成員函數(shù)的形參,是對象調(diào)用成員函數(shù)時,將對象地址作為實參傳遞給this形參。所以對象中不存儲this指針。
  4. this指針是成員函數(shù)第一個隱含的指針形參,一般情況由編譯器通過ecx寄存器自動傳遞,不需要用戶傳遞

【面試題】

  1. this指針存在哪里?

  2. this指針可以為空嗎?

我們先看下面的代碼,再回答這兩個問題

// 1.下面程序能編譯通過嗎?// 2.下面程序會崩潰嗎?在哪里崩潰class A{public:void PrintA(){	cout<<_a<<endl;}void Show(){	cout<<"Show()"<<endl;}private:int _a;};int main(){    Date* p = NULL;    p->PrintA();    p->Show();}

答案:p->PrintA()崩潰,p->Show()通過,因為PrintA中需要用到this指針,并對this指針指向的類的成員變量進行訪問,而p是一個空指針,沒有指向任何類,所以函數(shù)內(nèi)部對_a(實際上是this-> _a)的引用失敗

而對于p->Show(),Show函數(shù)在公共代碼段,所以p對其不用解引用,并且Show()函數(shù)中,并沒有對p進行解引用,所以不會引發(fā)錯誤。

最開始兩個問題的答案:

1.this指針是存放在棧上的,因為它是一個形參。

注:vs下是在ecx這個寄存器下存放的,因為this指針要被多次使用,所以把它放在寄存器中,方便快速訪問。

2.如上面代碼所示,this指針是可以為空的,只要在類成員函數(shù)內(nèi)不使用this指針。

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/122190.html

相關文章

  • Python方法(二) - 類與繼承

    摘要:在類內(nèi)部的方法中使用時。類的私有方法兩個下劃線開頭,聲明該方法為私有方法,不能在類地外部調(diào)用。先在本類中查找調(diào)用的方法,找不到才去基類中找。如果在繼承元組中列了一個以上的類,那么它就被稱作多重繼承。 類定義 類對象:創(chuàng)建一個類之后,可以通過類名訪問、改變其屬性、方法 實例對象:類實例化后,可以使用其屬性,可以動態(tài)的為實例對象添加屬性(類似javascript)而不影響類對象。 類...

    DevWiki 評論0 收藏0
  • javascript對象、類與原型鏈

    摘要:下面定義一個構(gòu)造函數(shù)不過這樣寫這個函數(shù),每個對象都會包括一部分,太浪費內(nèi)存。原型鏈與繼承在學習原型鏈之前我們一定要區(qū)分清楚是構(gòu)造函數(shù)的屬性,而是對象的屬性。但對象形式不等于基本類型。用來判斷對象是否某個構(gòu)造函數(shù)的實例。 js是一個基于對象的語言,所以本文研究一下js對象和類實現(xiàn)的過程和原理。 對象的屬性及屬性特性 下面是一個對象的各個部分: var person = { name:...

    adam1q84 評論0 收藏0
  • C++之繼承

    摘要:基類中的構(gòu)造函數(shù)和析構(gòu)函數(shù)不能被繼承,在派生類中需要定義新的構(gòu)造函數(shù)和析構(gòu)函數(shù),私有成員不能被繼承。對象訪問在派生類外部,通過派生類的對象對從基類繼承來的成員的訪問。 ...

    不知名網(wǎng)友 評論0 收藏0
  • JavaScript基礎: 類與繼承

    摘要:類的方法相當于之前我們定義在構(gòu)造函數(shù)的原型上。的構(gòu)造函數(shù)中調(diào)用其目的就是調(diào)用父類的構(gòu)造函數(shù)。是先創(chuàng)建子類的實例,然后在子類實例的基礎上創(chuàng)建父類的屬性。 前言   首先歡迎大家關注我的Github博客,也算是對我的一點鼓勵,畢竟寫東西沒法獲得變現(xiàn),能堅持下去也是靠的是自己的熱情和大家的鼓勵?!   ≡S久已經(jīng)沒有寫東西了,因為雜七雜八的原因最近一直沒有抽出時間來把寫作堅持下來,感覺和跑步一...

    liuchengxu 評論0 收藏0

發(fā)表評論

0條評論

alanoddsoff

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<