摘要:當(dāng)你用該日期類(lèi)創(chuàng)建一個(gè)對(duì)象時(shí),編譯器會(huì)自動(dòng)調(diào)用該構(gòu)造函數(shù)對(duì)新創(chuàng)建的變量進(jìn)行初始化。注意構(gòu)造函數(shù)的主要任務(wù)并不是開(kāi)空間創(chuàng)建對(duì)象,而是初始化對(duì)象。編譯器對(duì)內(nèi)置類(lèi)型使用默認(rèn)構(gòu)造函數(shù)時(shí),對(duì)其成員賦的是隨機(jī)值。
C語(yǔ)言是面向過(guò)程的,關(guān)注的是過(guò)程,分析出求解問(wèn)題的步驟,通過(guò)函數(shù)調(diào)用逐步解決問(wèn)題。
C++是基于面向?qū)ο蟮模P(guān)注的是對(duì)象,將一件事情拆分成不同的對(duì)象,靠對(duì)象之間的交互完成。
C語(yǔ)言中,結(jié)構(gòu)體中只能定義變量,在C++中,結(jié)構(gòu)體內(nèi)不僅可以定義變量,也可以定義函數(shù)。
struct Student{ void SetStudentInfo(const char* name, const char* gender, int age) { strcpy(_name, name); strcpy(_gender, gender); _age = age; } void PrintStudentInfo() { cout << _name << " " << _gender << " " << _age << endl; } char _name[20]; char _gender[3]; int _age;};
上面結(jié)構(gòu)體的定義,在C++中更喜歡用class來(lái)代替
class className{ // 類(lèi)體:由成員函數(shù)和成員變量組成 }; // 一定要注意后面的分號(hào)
class為定義類(lèi)的關(guān)鍵字,ClassName為類(lèi)的名字,{}中為類(lèi)的主體,注意類(lèi)定義結(jié)束時(shí)后面分號(hào)。
類(lèi)中的元素稱(chēng)為類(lèi)的成員:類(lèi)中的數(shù)據(jù)稱(chēng)為類(lèi)的屬性或者成員變量; 類(lèi)中的函數(shù)稱(chēng)為類(lèi)的方法或者成員函數(shù)。
聲明和定義全部放在類(lèi)體中,需要注意:成員函數(shù)如果在類(lèi)中定義,編譯器可能會(huì)將其當(dāng)成內(nèi)聯(lián)函數(shù)處
理
class Student{ void SetStudentInfo(const char* name, const char* gender, int age) { strcpy(_name, name); strcpy(_gender, gender); _age = age; } void PrintStudentInfo() { cout << _name << " " << _gender << " " << _age << endl; } char _name[20]; char _gender[3]; int _age;};
//person.hclass Person{public: //顯示信息 void show();public: char* _name; char* _sex; int _age;}//person.cpp#include"person.h>void Person::show(){ cout<<_name<<" "<<_sex<<" "<<_age<<endl;}
注意:一般情況下我們采用第二種方式
C++實(shí)現(xiàn)封裝的方式:用類(lèi)將對(duì)象的屬性與方法結(jié)合在一塊,讓對(duì)象更加完善,通過(guò)訪問(wèn)權(quán)限選擇性的將其
接口提供給外部的用戶(hù)使用。
【訪問(wèn)限定符說(shuō)明】
在類(lèi)和對(duì)象階段,我們只研究類(lèi)的封裝特性,那什么是封裝呢?
封裝本質(zhì)上是一種管理:我們使用類(lèi)將數(shù)據(jù)和方法都封裝起來(lái)。不想對(duì)外開(kāi)放的就用 protected/private 封裝起來(lái),用 public 封裝的成員允許外界對(duì)其進(jìn)行合理的訪問(wèn)。所以封裝本質(zhì)上是一種管理。
類(lèi)定義了一個(gè)新的作用域,類(lèi)的所有成員都在類(lèi)的作用域中。在類(lèi)體外定義成員,需要使用 :: 作用域解析符
指明成員屬于哪個(gè)類(lèi)域。
class Person{public: void PrintPersonInfo();private: char _name[20]; char _gender[3]; int _age;};// 這里需要指定PrintPersonInfo是屬于Person這個(gè)類(lèi)域void Person::PrintPersonInfo(){ cout<<_name<<" "_gender<<" "<<_age<<endl; }
用類(lèi)類(lèi)型創(chuàng)建對(duì)象的過(guò)程,稱(chēng)為類(lèi)的實(shí)例化
class Person{public: void PrintPersonInfo();private: char _name[20]; char _gender[3]; int _age;};void test(){ Person man; //類(lèi)的實(shí)例化 man._name="hehe"; man._age="66"; man._sex="男"; man._PrintPersonInfo();}
class A {public: void PrintA() { cout<<_a<<endl; }private: char _a;};
那么問(wèn)題來(lái)了?類(lèi)中既可以有成員變量,又可以有成員函數(shù),那么一個(gè)類(lèi)的對(duì)象中包含了什么?如何計(jì)算一個(gè)類(lèi)的大
小? 想要知道這個(gè),首先我們要弄明白類(lèi)在內(nèi)存中的存儲(chǔ)方式。
那為什么內(nèi)存要這樣存儲(chǔ)類(lèi)了?
原因:每個(gè)對(duì)象中成員變量是不同的,但是調(diào)用同一份函數(shù),如果按照此種方式存儲(chǔ),當(dāng)一個(gè)類(lèi)創(chuàng)建多
個(gè)對(duì)象時(shí),每個(gè)對(duì)象中都會(huì)保存一份代碼,相同代碼保存多次,浪費(fèi)空間。
結(jié)論:一個(gè)類(lèi)的大小,實(shí)際就是該類(lèi)中”成員變量”之和,當(dāng)然也要進(jìn)行內(nèi)存對(duì)齊,注意空類(lèi)的大小,空類(lèi)比
較特殊,編譯器給了空類(lèi)一個(gè)字節(jié)來(lái)唯一標(biāo)識(shí)這個(gè)類(lèi)。 如果有小伙伴不怎么明白內(nèi)存對(duì)齊:可以看看這篇文章:自定義類(lèi)型的知識(shí)點(diǎn)
我們先來(lái)定義一個(gè)日期類(lèi)Date
class Date{ public : void Display () { 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.Display(); d2.Display(); return 0; }
對(duì)于上述類(lèi),有這樣的一個(gè)問(wèn)題:
Date類(lèi)中有SetDate與Display兩個(gè)成員函數(shù),函數(shù)體中沒(méi)有關(guān)于不同對(duì)象的區(qū)分,那當(dāng)d1調(diào)用SetDate函數(shù)
時(shí),該函數(shù)是如何知道應(yīng)該設(shè)置d1對(duì)象,而不是設(shè)置d2對(duì)象呢?
C++中通過(guò)引入this指針解決該問(wèn)題,即:C++編譯器給每個(gè)“非靜態(tài)的成員函數(shù)“增加了一個(gè)隱藏的指針參
數(shù),讓該指針指向當(dāng)前對(duì)象(函數(shù)運(yùn)行時(shí)調(diào)用該函數(shù)的對(duì)象),在函數(shù)體中所有成員變量的操作,都是通過(guò)該
指針去訪問(wèn)。只不過(guò)所有的操作對(duì)用戶(hù)是透明的,即用戶(hù)不需要來(lái)傳遞,編譯器自動(dòng)完成
注意:this指針不能為空
下面來(lái)看一個(gè)例子
這里為什么會(huì)報(bào)錯(cuò)了?首先這個(gè)p是一個(gè)空指針,但是并不是對(duì)象是空指針就一定報(bào)錯(cuò),這里其實(shí)更重要的一個(gè)原因是PrintA里面為this->_a你對(duì)p進(jìn)行了訪問(wèn),而空指針是不能訪問(wèn)的。下面我們?cè)賮?lái)來(lái)p->Show()會(huì)不會(huì)報(bào)錯(cuò)?
如果一個(gè)類(lèi)中什么成員都沒(méi)有,我們簡(jiǎn)稱(chēng)其為空類(lèi)。但是空類(lèi)中真的什么都沒(méi)有嗎?其實(shí)不然,任何一個(gè)類(lèi),即使我們什么都不寫(xiě),類(lèi)中也會(huì)自動(dòng)生成6個(gè)默認(rèn)成員函數(shù)。
class Date {}; //空類(lèi)
class Date{public: Date(int year = 0, int month = 1, int day = 1)// 構(gòu)造函數(shù) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "年" << _month << "月" << _day << "日" << endl; }private: int _year; int _month; int _day;};
例如,上述日期類(lèi)中的成員函數(shù)Date就是一個(gè)構(gòu)造函數(shù)。當(dāng)你用該日期類(lèi)創(chuàng)建一個(gè)對(duì)象時(shí),編譯器會(huì)自動(dòng)調(diào)用該構(gòu)造函數(shù)對(duì)新創(chuàng)建的變量進(jìn)行初始化。
注意:構(gòu)造函數(shù)的主要任務(wù)并不是開(kāi)空間創(chuàng)建對(duì)象,而是初始化對(duì)象。(這兒可以先暫時(shí)這么理解)
class Date{public: // 1.無(wú)參構(gòu)造函數(shù) Date () {} Date(int year = 0, int month = 1, int day = 1)// 構(gòu)造函數(shù) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "年" << _month << "月" << _day << "日" << endl; }private: int _year; int _month; int _day;};void TestDate(){ Date d1; // 調(diào)用無(wú)參構(gòu)造函數(shù) Date d2 (2015, 1, 1); // 調(diào)用帶參的構(gòu)造函數(shù) // 注意:如果通過(guò)無(wú)參構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),對(duì)象后面不用跟括號(hào),否則就成了函數(shù)聲明 // 以下代碼的函數(shù):聲明了d3函數(shù),該函數(shù)無(wú)參,返回一個(gè)日期類(lèi)型的對(duì)象 Date d3(); }
class Date{public: /* // 如果用戶(hù)顯式定義了構(gòu)造函數(shù),編譯器將不再生成 Date (int year, int month, int day) { _year = year; _month = month; _day = day; } */private: int _year; int _month; int _day;};void Test(){ // 沒(méi)有定義構(gòu)造函數(shù),對(duì)象也可以創(chuàng)建成功,因此此處調(diào)用的是編譯器生成的默認(rèn)構(gòu)造函數(shù) Date d; }
// 默認(rèn)構(gòu)造函數(shù)class Date{ public: Date() { _year = 1900 ; _month = 1 ; _day = 1; } Date (int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; }private : int _year ; int _month ; int _day ;};// 以下測(cè)試函數(shù)能通過(guò)編譯嗎?void Test(){ Date d1; }
顯然這兒是過(guò)不了的,因?yàn)轭?lèi)中有多個(gè)默認(rèn)函數(shù)。
7.編譯器對(duì)內(nèi)置類(lèi)型使用默認(rèn)構(gòu)造函數(shù)時(shí),對(duì)其成員賦的是隨機(jī)值。但對(duì)自定義類(lèi)型,會(huì)調(diào)用它的默認(rèn)函數(shù)。
這兒并沒(méi)有我們自己寫(xiě)的構(gòu)造函數(shù),所以編譯時(shí)會(huì)調(diào)用默認(rèn)的構(gòu)造函數(shù),又由于類(lèi)成員都是內(nèi)置類(lèi)型,因此賦的都是隨機(jī)值。下面我們?cè)賮?lái)看看自定義類(lèi)型。
注意:如果你Time類(lèi)中沒(méi)有自己寫(xiě)構(gòu)造函數(shù),用編譯器默認(rèn)的構(gòu)造函數(shù),它也是一樣會(huì)輸入隨機(jī)值的。
前面通過(guò)構(gòu)造函數(shù)的學(xué)習(xí),我們知道一個(gè)對(duì)象時(shí)怎么來(lái)的,那一個(gè)對(duì)象又是怎么沒(méi)呢的?
析構(gòu)函數(shù):與構(gòu)造函數(shù)功能相反,析構(gòu)函數(shù)不是完成對(duì)象的銷(xiāo)毀,局部對(duì)象銷(xiāo)毀工作是由編譯器完成的。而
對(duì)象在銷(xiāo)毀時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成類(lèi)的一些資源清理工作。
構(gòu)造函數(shù):只有單個(gè)形參,該形參是對(duì)本類(lèi)類(lèi)型對(duì)象的引用(一般常用const修飾),在用已存在的類(lèi)類(lèi)型對(duì)象 創(chuàng)建新對(duì)象時(shí)由編譯器自動(dòng)調(diào)用
#include using namespace std;class Date{public: Date(int year = 0, int month = 1, int day = 1)// 構(gòu)造函數(shù) { _year = year; _month = month; _day = day; } Date(const Date& d)// 拷貝構(gòu)造函數(shù) ,與構(gòu)造函數(shù)形成函數(shù)重載 { _year = d._year; _month = d._month; _day = d._day; }private: int _year; int _month; int _day;};int main(){ Date d1(2021, 9, 27); Date d2(d1); // 用已存在的對(duì)象d1創(chuàng)建對(duì)象d2 return 0;}
因此通過(guò)形參不寫(xiě)成引用的形式,會(huì)形成無(wú)限遞歸。
一般涉及到堆區(qū)的問(wèn)題,淺拷貝是無(wú)法解決問(wèn)題的。
下面我們來(lái)舉個(gè)例子:
class Stack{public: Stack(int capacity = 4) { _ps = (int*)malloc(sizeof(int)* capacity); _size = 0; _capacity = capacity; } void Print() { cout << _ps << endl;// 打印棧空間地址 }private: int* _ps; int _size; int _capacity;};int main(){ Stack s1; s1.Print();// 打印s1棧空間的地址 Stack s2(s1);// 用已存在的對(duì)象s1創(chuàng)建對(duì)象s2 s2.Print();// 打印s2棧空間的地址 return 0;}
我們可以看到,類(lèi)中沒(méi)有自己定義拷貝構(gòu)造函數(shù),那么當(dāng)我們用已存在的對(duì)象來(lái)創(chuàng)建另一個(gè)對(duì)象時(shí),將調(diào)用編譯器自動(dòng)生成的拷貝構(gòu)造函數(shù)。這段代碼中,我們的本意是用已存在的對(duì)象s1創(chuàng)建對(duì)象s2,但編譯器自動(dòng)生成的拷貝構(gòu)造函數(shù),完成的是淺拷貝,拷貝出來(lái)的對(duì)象s2將不能滿(mǎn)足我們的要求。
結(jié)果打印s1棧和s2棧空間的地址相同,這就意味著,就算在創(chuàng)建完s2棧后,我們對(duì)s1棧做的任何操作都會(huì)直接影響到s2棧。
?
這個(gè)時(shí)候問(wèn)題就很?chē)?yán)重了。首先我們對(duì)s1的修改都會(huì)直接影響s2,而且更重要的一個(gè)是:我們對(duì)它們共同指向的那塊空間進(jìn)行了兩次的析構(gòu),會(huì)造成空間多次釋放的問(wèn)題。
C++為了增強(qiáng)代碼的可讀性引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有其返回值類(lèi)
型,函數(shù)名字以及參數(shù)列表,其返回值類(lèi)型與參數(shù)列表與普通的函數(shù)類(lèi)似。
函數(shù)名字為:關(guān)鍵字operator后面接需要重載的運(yùn)算符符號(hào)。
函數(shù)原型:返回值類(lèi)型 operator操作符(參數(shù)列表)
注意:
1.不能通過(guò)連接其他符號(hào)來(lái)創(chuàng)建新的操作符:比如operator@
2.重載操作符必須有一個(gè)類(lèi)類(lèi)型或者枚舉類(lèi)型的操作數(shù)
3.用于內(nèi)置類(lèi)型的操作符,其含義不能改變,例如:內(nèi)置的整型+,不 能改變其含義
4.作為類(lèi)成員的重載函數(shù)時(shí),其形參看起來(lái)比操作數(shù)數(shù)目少1成員函數(shù)的
操作符有一個(gè)默認(rèn)的形參this,限定為第一個(gè)形參
5.* 、:: 、sizeof 、?: 、. 注意以上5個(gè)運(yùn)算符不能重載。這個(gè)經(jīng)常在筆試選擇題中出現(xiàn)。
bool operator==(const Date& d1, const Date& d2) { return d1._year == d2._year; && d1._month == d2._month && d1._day == d2._day; }
對(duì)于這個(gè)重載的函數(shù),你可以定義再類(lèi)里面,這樣就少一個(gè)參數(shù),因?yàn)橛衪his指針的存在。你也可以定義在外面,但是定義在外面時(shí),可能你的類(lèi)成員時(shí)private封裝的,無(wú)法訪問(wèn)到,這時(shí)有兩個(gè)解決辦法:一是把類(lèi)成員用public封裝,二是用友元函數(shù)(之后會(huì)講到)。
Date& operator=(const Date& d)// 賦值運(yùn)算符重載函數(shù) { if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; }
這里為什么要返回引用了?如果你去測(cè)試發(fā)現(xiàn)D1=D2,如果你的返回值是Date的話,似乎也能過(guò),但是如果你的測(cè)試用例是D1=D2=D3的話,那就一定過(guò)不了了,因?yàn)槟悴皇欠祷氐膶?duì)象本身,無(wú)法形成鏈?zhǔn)骄幊蹋@也是為什么這兒返回*this的原因,因?yàn)閠his是指向左操作符的。
其他一些運(yùn)算符的重載這兒就不多說(shuō)了,有興趣的小伙伴可以自己去嘗試嘗試。下面來(lái)說(shuō)幾個(gè)重載運(yùn)算符時(shí)的注意點(diǎn)。
重載賦值運(yùn)算符需要注意以下幾點(diǎn):
一、參數(shù)類(lèi)型設(shè)置為引用,并用const進(jìn)行修飾
賦值運(yùn)算符重載函數(shù)的第一個(gè)形參默認(rèn)是this指針,第二個(gè)形參是我們賦值運(yùn)算符的右操作數(shù)。
由于是自定義類(lèi)型傳參,我們?nèi)羰鞘褂脗髦祩鲄ⅲ瑫?huì)額外調(diào)用一次拷貝構(gòu)造函數(shù),所以函數(shù)的第二個(gè)參數(shù)最好使用引用傳參(第一個(gè)參數(shù)是默認(rèn)的this指針,我們管不了)。
其次,第二個(gè)參數(shù),即賦值運(yùn)算符的右操作數(shù),我們?cè)诤瘮?shù)體內(nèi)不會(huì)對(duì)其進(jìn)行修改,所以最好加上const進(jìn)行修飾。
二、返回值使用引用返回
原因在=運(yùn)算符重載中說(shuō)過(guò)了,為了返回對(duì)象自身,形成鏈?zhǔn)骄幊獭#╮eturn *this才是返回自身,不要忘記解引用哦)
三、一個(gè)類(lèi)如果沒(méi)有顯示定義賦值運(yùn)算符重載,編譯器也會(huì)自動(dòng)生成一個(gè),完成對(duì)象按字節(jié)序的值拷貝
沒(méi)錯(cuò),賦值運(yùn)算符重載編譯器也可以自動(dòng)生成,并且也是支持連續(xù)賦值的。但是編譯器自動(dòng)生成的賦值運(yùn)算符重載完成的是對(duì)象按字節(jié)序的值拷貝,例如d2 = d1,編譯器會(huì)將d1所占內(nèi)存空間的值完完全全地拷貝到d2的內(nèi)存空間中去,類(lèi)似于memcpy。
但是有些類(lèi)就不行了,所以有些類(lèi)還是要我們自己寫(xiě)賦值運(yùn)算符重載的。
注意區(qū)分拷貝和賦值:
Date d1(2021, 6, 1); Date d2(d1); Date d3 = d1;
這里一個(gè)三句代碼,我們現(xiàn)在都知道第二句代碼調(diào)用的是拷貝構(gòu)造函數(shù),那么第三句代碼呢?調(diào)用的是哪一個(gè)函數(shù)?是賦值運(yùn)算符重載函數(shù)嗎?
其實(shí)第三句代碼調(diào)用的也是拷貝構(gòu)造函數(shù),注意區(qū)分拷貝構(gòu)造函數(shù)和賦值運(yùn)算符重載函數(shù)的使用場(chǎng)景:
拷貝構(gòu)造函數(shù):用一個(gè)已經(jīng)存在的對(duì)象去構(gòu)造初始化另一個(gè)即將創(chuàng)建的對(duì)象。
賦值運(yùn)算符重載函數(shù):在兩個(gè)對(duì)象都已經(jīng)存在的情況下,將一個(gè)對(duì)象賦值給另一個(gè)對(duì)象。
我們將const修飾的類(lèi)成員函數(shù)稱(chēng)之為const成員函數(shù),const修飾類(lèi)成員函數(shù),實(shí)際修飾的是類(lèi)成員函數(shù)隱含的this指針,表明在該成員函數(shù)中不能對(duì)this指針指向的對(duì)象進(jìn)行修改。
例如,我們可以對(duì)類(lèi)成員函數(shù)中的打印函數(shù)進(jìn)行const修飾,避免在函數(shù)體內(nèi)不小心修改了對(duì)象:
void Print()const// cosnt修飾的打印函數(shù) { cout << _year << "年" << _month << "月" << _day << "日" << endl; }
注意:
在使用const時(shí)要注意,權(quán)限不能放大,但是可以縮小。
在創(chuàng)建對(duì)象時(shí),編譯器會(huì)通過(guò)調(diào)用構(gòu)造函數(shù),給對(duì)象中的各個(gè)成員變量一個(gè)合適的初始值:
class Date{public: // 構(gòu)造函數(shù)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/121415.html
文章目錄 強(qiáng)烈推薦系列教程,建議學(xué)起來(lái)!! 一.pycharm下載安裝二.python下載安裝三.pycharm上配置python四.配置鏡像源讓你下載嗖嗖的快4.1pycharm內(nèi)部配置 4.2手動(dòng)添加鏡像源4.3永久配置鏡像源 五.插件安裝(比如漢化?)5.1自動(dòng)補(bǔ)碼神器第一款5.2漢化pycharm5.3其它插件 六.美女背景七.自定義腳本開(kāi)頭八、這個(gè)前言一定要看九、pyt...
摘要:于是乎,冰河寫(xiě)了一個(gè)腳本完美去除了桌面圖標(biāo)煩人的小箭頭。今天,給大家分享一個(gè)如何完美去除桌面快捷圖標(biāo)小箭頭的技巧,希望能夠給大家?guī)?lái)幫助。這種方法不會(huì)導(dǎo)致任何問(wèn)題可放心使用,冰河已經(jīng)親自測(cè)試過(guò)了。 ...
人生苦短,我用Python 開(kāi)發(fā)環(huán)境搭建安裝 Python驗(yàn)證是否安裝成功安裝Pycharm配置pycharm 編碼規(guī)范基本語(yǔ)法規(guī)則保留字單行注釋多行注釋行與縮進(jìn)多行語(yǔ)句數(shù)據(jù)類(lèi)型空行等待用戶(hù)輸入print輸出 運(yùn)算符算術(shù)運(yùn)算符邏輯運(yùn)算符成員運(yùn)算符身份運(yùn)算符運(yùn)算符優(yōu)先級(jí) 字符串訪問(wèn)字符串中的值字符串更新合并連接字符串刪除空白startswith()方法endswith()方法字符串格式化...
閱讀 2949·2021-10-28 09:32
閱讀 2980·2021-10-11 10:57
閱讀 3125·2021-10-08 10:05
閱讀 2606·2021-09-28 09:36
閱讀 2221·2019-08-30 15:55
閱讀 2276·2019-08-30 15:44
閱讀 2401·2019-08-30 14:02
閱讀 3082·2019-08-29 17:16