您好,登錄后才能下訂單哦!
這篇文章主要介紹c++中primer類怎么用,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!
類的基本思想是數(shù)據(jù)抽象和封裝。
數(shù)據(jù)抽象是依賴接口和實現(xiàn)分離的編程技術(shù)。
成員函數(shù)的聲明必須在類內(nèi)部,定義可以在內(nèi)部或外部
作為接口的非成員函數(shù),如print、read,聲明定義都在類的外部。
定義在類內(nèi)部的函數(shù)都是隱式的inline函數(shù)
調(diào)用一個成員函數(shù)時,隱式初始化this指針
任何自定義名為this的參數(shù)或者變量都是非法的
const成員函數(shù)
const成員函數(shù):在參數(shù)列表后加上const關(guān)鍵字的函數(shù)
const的作用是修改隱式this指針的類型
默認(rèn)情況下,this的類型是指向類型非常量的常量指針。因此,不能將this綁定在一個非常量對象上(不能把this綁定到其他對象),所以也不能在常量對象上調(diào)用普通成員函數(shù)(不能用const 對象訪問普通成員函數(shù))。
const成員函數(shù)提高了函數(shù)靈活性
常量對象,以及常量對象的引用或指針只能調(diào)用常量成員函數(shù)。
編譯器分兩步處理類。
1.編譯成員聲明。
2.所有成員聲明編譯完后,編譯成員函數(shù)體。因此,成員聲明出現(xiàn)在成員函數(shù)體后,編譯器也可以正常編譯
在類外定義函數(shù)體
需要在函數(shù)名前加上類名::,在類名之后剩余的代碼位于作用域之內(nèi)
若返回類型也是在類內(nèi)聲明的,就需要在函數(shù)名和返回類型前都加上類名::。
若在類內(nèi)聲明成了const成員函數(shù),在外部定義時,const關(guān)鍵字也不能省略。
若需要返回類本身,使用return *this
類相關(guān)非成員函數(shù):屬于類的接口,但是不屬于類本身。
通常把函數(shù)聲明和定義分開。和類聲明在同一頭文件內(nèi)。
通常情況下,拷貝一個類其實是拷貝其成員。(若想拷貝執(zhí)行其他操作,查閱拷貝賦值函數(shù))
Sale_data s1; Sale_data s2=s1;//s2拷貝了s1的成員
構(gòu)造函數(shù)的任務(wù)是初始化類對象的數(shù)據(jù)成員
只要類對象被創(chuàng)建,一定會執(zhí)行構(gòu)造函數(shù)
構(gòu)造函數(shù)名與類名相同,并且沒有返回類型,其他與普通函數(shù)相同。
構(gòu)造函數(shù)不能聲明成const
默認(rèn)構(gòu)造函數(shù)
若類包含內(nèi)置類型或復(fù)合類型成員,只有當(dāng)這些值全被賦予了類內(nèi)初始值時,這個類才適合使用合成的默認(rèn)構(gòu)造函數(shù)。
若類a包含一個成員類b,若b沒有默認(rèn)構(gòu)造函數(shù),則編譯器無法為a構(gòu)造正確的默認(rèn)構(gòu)造函數(shù)
若定義了其他構(gòu)造函數(shù),則編譯器不會構(gòu)造默認(rèn)初始函數(shù)
若存在類內(nèi)初始值,用它來初始化成員
否則,默認(rèn)初始化成員
默認(rèn)構(gòu)造函數(shù)無需任何實參
若沒有為類顯式定義任何構(gòu)造函數(shù),編譯器隱式構(gòu)造一個合成的默認(rèn)構(gòu)造函數(shù)。
合成的默認(rèn)構(gòu)造函數(shù)按照如下規(guī)則初始化類成員
某些類不能依賴合成的默認(rèn)構(gòu)造函數(shù)
class A{ //定義了一個實參為string的構(gòu)造函數(shù) //此時,編譯器不會合成默認(rèn)構(gòu)造函數(shù) A(std::string a){} } A a;//錯誤,沒有默認(rèn)構(gòu)造函數(shù) A a1(std::string("小黑"));//只能用string參數(shù)
參數(shù)列表后加上 =defualt表示要求編譯器生成默認(rèn)構(gòu)造函數(shù)
=defualt
可以和聲明一起出現(xiàn)在類內(nèi),也可以作為定義出現(xiàn)在類外。
若在類內(nèi)部,則默認(rèn)構(gòu)造函數(shù)時內(nèi)聯(lián)的,若在類外部,默認(rèn)不是內(nèi)聯(lián)的。
class A{ A()=defualt; } A a;//正確,編譯器生成默認(rèn)構(gòu)造函數(shù)
構(gòu)造函數(shù)初始值列表
存在編譯器不支持類內(nèi)初始值,這樣的話默認(rèn)構(gòu)造函數(shù)不適用(因為默認(rèn)構(gòu)造函數(shù)使用類內(nèi)初始值初始化類成員),這時應(yīng)該使用構(gòu)造函數(shù)初始值列表。
函數(shù)初始值列表是參數(shù)列表如下所示(冒號以及冒號和花括號間的代碼::bookNo(s
))
構(gòu)造函數(shù)不應(yīng)該輕易覆蓋掉類內(nèi)初始值,除非新賦的值與原值不同在
構(gòu)造函數(shù)的過程中,沒有出現(xiàn)在函數(shù)初始化列表中的成員將被執(zhí)行默認(rèn)初始化
class Sales_data{ Sales_data(const std::string &s,unsigned n,double p): bookNo(s),units_sold(n),revenue(p*n){} //當(dāng)編譯器不支持類內(nèi)初始值時,可用如下方法定義 Sales_data(const std::string &s): bookNo(s),units_sold(0),revenue(0){} }
在類外部定義構(gòu)造函數(shù),要聲明是哪個類的構(gòu)造函數(shù),在函數(shù)名前加上類名::
Sales_data::Sales_data(std::istream cin){ read(cin,*this); }
編譯器會為類合成拷貝、賦值和銷毀操作。
編譯器生成的版本對對象的每個成員執(zhí)行拷貝、賦值和銷毀操作
訪問說明符
public說明符后的成員在整個程序內(nèi)可以被訪問
private說明符后的成員可以被類的成員函數(shù)訪問
一個類可以包含0個或多個訪問說明符,有效范圍到下一個說明符出現(xiàn)為止。
class和struct關(guān)鍵字定義類的唯一區(qū)別是
class在第一個訪問說明符出現(xiàn)之前的區(qū)域默認(rèn)是private
struct在第一個訪問說明符出現(xiàn)之前的區(qū)域默認(rèn)是public
類可以允許其他類或函數(shù)訪問他的非公有成員。方法是用關(guān)鍵字friend聲明友元。
友元的聲明只能在類內(nèi)部
友元聲明的位置不限,最好在類定義開始或結(jié)束前集中聲明友元。
封裝的好處
確保用戶代碼不會無意間破壞封裝對象的狀態(tài)
被封裝的類的具體實現(xiàn)細(xì)節(jié)可以隨時改變
友元在類內(nèi)的聲明僅僅指定了訪問權(quán)限,并不是一個通常意義的函數(shù)聲明
若希望類的用戶能夠調(diào)用某個友元函數(shù),需要在友元聲明之外再專門對函數(shù)進(jìn)行一次聲明
為了使友元對類用戶可見,友元聲明與類本身防止在同一個頭文件中
一些編譯器強制限定友元函數(shù)必須在使用之前在類的外部聲明
接下來介紹:類型成員、類的成員的類內(nèi)初始值、可變數(shù)據(jù)成員、內(nèi)聯(lián)成員函數(shù)、從成員函數(shù)返回*this
、如何定義使用類類型、友元類
類別名(類型成員):
在類中定義的類型名字和其他成員一樣存在訪問限制,可以是public或者private
類別名必須先定義后使用
(回憶:類成員變量可以在類成員函數(shù)之后定義,但是在類函數(shù)中使用,原因是編譯器先編譯類成員變量后邊一類成員函數(shù))
類型成員通常出現(xiàn)在類開始的地方
class Screen{ public: //等價于 using pos = std::string::size_type typedef std::string::size_type pos; }
令成員作為內(nèi)聯(lián)函數(shù)
定義在類內(nèi)部的函數(shù)是自動inline的,定義在類外部的函數(shù),若需要聲明內(nèi)聯(lián)函數(shù),要加上inline;inline成員函數(shù)也應(yīng)該和相應(yīng)的類定義在同一個頭文件夾
inline Screen& Screen::move(pos r,pos c){ pos row = r*width; cursor = row + c; return *this; }
可變數(shù)據(jù)成員,永遠(yuǎn)不會是const,即使他是const對象的成員
class Screen{ public void some_member() const; private: mutable size_t access_ctr;//使用mutable聲明可變數(shù)據(jù)成員 } void Screen::some_member() const { ++access_ctr;//即使在const成員函數(shù)中,仍然可以修改可變數(shù)據(jù)成員 }
類內(nèi)初始值使用=的初始化形式或者花括號括起來的直接初始化形式
注意返回類型是否是引用。是否是引用對函數(shù)的使用方法影響很大
inline Screen &Screen::set(char ch){ content[cursor] =ch; return *this; } inline Screen &Screen ::move(pos r,pos col){ cursor= r * width + col ; return *this; } Screen s(3,2,''); //move函數(shù)返回s本身,所以可以接著調(diào)用set函數(shù) //并且move函數(shù)返回的是Screen的引用,若返回的不是引用,則會返回一個新的Screen對象 s.move(3,2).set('!');
從const函數(shù)返回的是常量引用,在const函數(shù)中無法修改類成員變量
使用const函數(shù)進(jìn)行重載
編寫函數(shù)display打印Screen中的contents,因為只是展示,不需要修改值,所以這應(yīng)該是一個const函數(shù)。
但是希望實現(xiàn)在展示后,能移動光標(biāo):s.display().move(2,3)。這要求display返回的值是可以修改的,所以這不應(yīng)該是const函數(shù)。
基于const重載,可以根據(jù)Screen對象是否是const來進(jìn)行重載。
建議多使用do_display這類函數(shù)完成實際工作,使公共代碼使用私有函數(shù)
可以集中修改
沒有額外開銷
class Screen{ public: Screen* display(std::ostream &os){ do_display(os); return *this; } const Screen* display(std::ostream &os) const{ do_display(os); return *this; } private: void do_display(std::ostream &os) const{ os<<content; } } int main(){ const Screen cs(3,3,'!'); Screen s(3,3,'.') cs.display();//因為cs是const的,調(diào)用第二個const函數(shù) s.display();//調(diào)用第一個非const的函數(shù) }
每個類定義了唯一的類型,即使成員完全相同,也是不一樣的類。
class A{ int member; } class B{ int member; } A a; B b = a;//錯誤?。?/pre>
不完全類型
類似于函數(shù),類也可以只聲明,不定義,這被叫做不完全類型
不完全類型是向程序說明這是一個類名
不完全類型使用環(huán)境很有限,只是可以定義指向這種類型的指針或引用,聲明(但不能定義)以不完全類型作為參數(shù)或返回類型的函數(shù)。
類在創(chuàng)建前必須被定義
類的成員不能有類本身(除了后面介紹的static類),但是可以是指向自身的引用或指針
一個類制定了其友元類,則友元函數(shù)可以訪問該類的所有成員
友元關(guān)系不存在傳遞性
每個類自己負(fù)責(zé)控制自己的友元類或友元函數(shù)
定義友元函數(shù)的順序:
有一個screen類,有私有成員content;
有clear函數(shù),可以清除content的內(nèi)容。
1.先聲明clear函數(shù)
2.在screen類中將clear函數(shù)函數(shù)定義為友元函數(shù)
3.定義clear函數(shù),使用screen類
定義友元類
有類window,window有私有成員content;友元類 window_mgr需要直接操作content。
正常編寫window類,在window類中聲明:friend class window_mgr;
正常編寫 window_mgr類,可以直接使用window的content
注意將類寫在頭文件中,要按照如下格式;否則編譯會報錯重復(fù)的類定義
#ifndef xxx_H #define xxx_H /class 定義/// #endif
一個類想把一組重載函數(shù)定義為它的友元,需要對這組函數(shù)中的每一個進(jìn)行友元聲明。
友元聲明僅僅表示對友元關(guān)系的聲明,但并不表示友元這個函數(shù)本身的聲明
struct X{ friend viod f(){/*友元函數(shù)可以定義在類的內(nèi)部,但是我認(rèn)為這樣沒有意義*/ X(){f();}//錯誤,f還沒有被定義 void g(); void h(); } void X::g(){ return f();}//錯誤,f還沒有被定義 void f(); void X::h(){return f();}//正確,f的聲明已經(jīng)在定義中了 };
定義在類外的方法需要在方法名前使用::說明該方法屬于哪一個類,在說明屬于的類后,該函數(shù)的作用域位于該類內(nèi)。
即返回類型使用的名字位于類的作用域之外。若返回類型也是類的成員,需要在返回類型前使用::指明返回類型屬于的類
//pos的類型聲明在window類中,并且返回類型在類的作用域外,因此要使用window::pos window::pos window::get_pos(){ //在window::get_pos后的所有代碼作用域在類內(nèi),所以返回cursor,相當(dāng)于this->cursor return cursor; }
類的定義分兩步處理
1.編譯成員的聲明
2.直到類成員全部可見后編譯函數(shù)體
一般來說,內(nèi)層作用域可以重新定義外層作用域名字;但在類中若使用了某個外層作用域中的名字,并且該名字表示一種類型,則類不能在之后重新定義該名字
typedef double Money; class Acount{ public: Money balace(){return bal;}//使用外層定義的Money private: typedef double Money;//錯誤,不能重新定義Money Money bal; }
類型名的定義通常出現(xiàn)在類的開始處,來確保所有使用該類型的成員都出現(xiàn)在定義之后;
類中同名變量會被隱藏,但是可以用this指針訪問成員變量
double height; class Window{ double height; } void Window::dummy_fcn(double height){ double class_height = this->height; double para_height = height; double global_height = ::height; }
構(gòu)造函數(shù)初始值列表 在類的有引用成員和const成員時,必須在構(gòu)造函數(shù)中使用初始值列表進(jìn)行初始化
建議養(yǎng)成使用構(gòu)造函數(shù)初始值的習(xí)慣
初始值列表只說明初始值成員的值,并不限定初始值的具體執(zhí)行順序;初始值順序與他們在類定義中的出現(xiàn)順序一致(某些編譯器在初始值列表和類中順序不一致時,會生成一條警告信息)
//示例危險操作 strcut X{ //實際上按照聲明順序初始化,先初始化rem時,base的值未知 X(int i,int j):base(i),rem(base%j){} int rem,base; }
建議,使用初始值列表和類中變量順序一致,可能的話,盡量避免使用某些成員初始化其他成員。
成員初始值列表的唯一入口是類名,可以用構(gòu)造函數(shù)可以調(diào)用其他構(gòu)造函數(shù),調(diào)用過程應(yīng)該寫在初始值列表位置
class Sale_data{ public: Sales_data(const std::string &s,unsigned s_num,double price):units_sold(s_num),revenue(s_num*price),BookNo(s){} Sales_data():Sales_data("",0,0){}//委托給上一個構(gòu)造函數(shù) }
當(dāng)對象被默認(rèn)初始化或值初始化時執(zhí)行默認(rèn)構(gòu)造參數(shù)
默認(rèn)初始化發(fā)生在:
1.塊作用域內(nèi)不使用任何初始值定義的一個非靜態(tài)變量或者數(shù)組時
2.類本身含有類類型的成員且使用合成的默認(rèn)構(gòu)造函數(shù)
3.類類型成員沒有在構(gòu)造函數(shù)初始值列表中顯式初始化時
值初始化發(fā)生在:
1.初始化數(shù)組時,提供的初始值數(shù)量少于數(shù)組大小
2.不使用初始值定義一個局部變量時
3.書寫形如T()的表達(dá)式顯式請求值初始化時,其中T是類型名。如vector接受一個實參說明vector的大小
若定義了其他構(gòu)造函數(shù),編譯器不在生成默認(rèn)構(gòu)造函數(shù),因此最好需要我們程序員來提供一個構(gòu)造函數(shù)
轉(zhuǎn)換構(gòu)造函數(shù):能夠通過一個實參調(diào)用的構(gòu)造函數(shù)定義一條從構(gòu)造函數(shù)的參數(shù)構(gòu)造類型向類類型轉(zhuǎn)換的規(guī)則。
vector<string> str_vec; //需要push一個string,但傳參一個字符串。這里使用了string的轉(zhuǎn)換構(gòu)造函數(shù) str_vec.push_back("小黑~");
轉(zhuǎn)換構(gòu)造函數(shù)只允許一步構(gòu)造轉(zhuǎn)換
需要多個參數(shù)的構(gòu)造函數(shù)無法執(zhí)行隱式轉(zhuǎn)換
//Sales_data有參數(shù)為string的構(gòu)造函數(shù) //Sales_data的combine為方法: //Sales_data & Sales_data::combine(const Sales_data& ); Sales_data item; item.combine("無限~")//錯誤,只允許一步構(gòu)造 item.combine(string("無限~"))//正確,只有string到Sales_data的一步隱式構(gòu)造轉(zhuǎn)換
使用explicit
抑制構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換
class Sales_data{ explicit Sales_data(const string&):bookNo(s){}; ...//其他聲明 } item.combine(string("無限~"));//錯誤,explicit阻止了隱式轉(zhuǎn)換
explicit
函數(shù)只能用于直接初始化
//Sales_data的構(gòu)造函數(shù):explicit Sales_data(const string&):bookNo(s){}; string bookNo = "001"; Sales_data item1(bookNo);//正確,直接初始化 Sales_data item2 = bookNo;//錯誤,拷貝初始化
盡管編譯器不會將explicit
的構(gòu)造函數(shù)用于隱式轉(zhuǎn)換過程,但是可以使用顯式強制轉(zhuǎn)化
string bookNo ="001"; item.combine(bookNo);//錯誤,explicit阻止了隱式轉(zhuǎn)換 item.combine(static_cast<Sales_data>(bookNo));//正確,強制轉(zhuǎn)換
聚合類的定義。一個滿足下列條件的類被稱為聚合類
1.所有成員都是public的
2.沒有定義任何構(gòu)造函數(shù)
3.沒有類內(nèi)初始值
4.沒有基類,也沒有virtual函數(shù)
可以使用{}括起來的成員初始值列表來初始化聚合類
class Data{ public: int ival; string s; } //順序一定相同 Data val1={0,"孔子"};
constexpr函數(shù)的參數(shù)和返回值必須是字面值類型
字面值類型包括:算術(shù)類型、指針、引用、數(shù)據(jù)成員都是字面值類型的聚合類和滿足下面條件的類。
1.數(shù)據(jù)成員都是字面值類型
2.類必須含有一個 constexpr構(gòu)造函數(shù)
3.使用默認(rèn)定義的析構(gòu)函數(shù)
4.如果一個數(shù)據(jù)成員含有類內(nèi)初始值,則該初始值必須是一條常量表達(dá)式;如果數(shù)據(jù)成員屬于某種類類型,則初始值必須使用自己的constexpr構(gòu)造函數(shù)
構(gòu)造函數(shù)不能是const的,但是可以是constexpr的。
字面值常量類,至少提供一個constexpr構(gòu)造函數(shù)
constexpr構(gòu)造函數(shù)
是構(gòu)造函數(shù),沒有返回語句
是 constexpr函數(shù),唯一可執(zhí)行語句就是返回語句
所以constexpr構(gòu)造函數(shù)函數(shù)體為空,只能通過初始化列表值來執(zhí)行構(gòu)造初始化
class Data{ public: constexpr Data(int para_i,string para_s):ival(para_i),s(para_s){} int ival; string s; } constexpr Data data(1,"吃烤肉");
使用static在類中聲明靜態(tài)成員,該靜態(tài)成員和類關(guān)聯(lián),而不是和類對象關(guān)聯(lián)
靜態(tài)成員函數(shù)也不與任何類對象綁定起來,并且靜態(tài)成員函數(shù)不含this指針。(包括this的顯式調(diào)用和對非靜態(tài)成員的隱式調(diào)用)
在外部定義static函數(shù)時,不能重復(fù)static,static關(guān)鍵字出現(xiàn)類內(nèi)部的聲明語句
不能在類內(nèi)部初始化靜態(tài)成員,必須在類的外部定義和初始化每個靜態(tài)成員,因此一旦被定義,就一直存在在程序的整個生命周期
想要確保對象只定義一次,最好的辦法就是把靜態(tài)數(shù)據(jù)成員的定義和其他非內(nèi)聯(lián)函數(shù)的定義放在同一個文件
靜態(tài)成員的類內(nèi)初始化
一般情況下, 類的靜態(tài)成員不應(yīng)該在類的內(nèi)部初始化
靜態(tài)成員必須是字面值常量類型(constexpr)
即使一個常量靜態(tài)數(shù)據(jù)成員在類的內(nèi)部初始化了,通常也應(yīng)該放在類的外部定義一下該成員(這樣才能使生命周期直到程序結(jié)束)。
靜態(tài)成員的特殊使用場景
靜態(tài)數(shù)據(jù)成員的類型可以就是它所屬的類型
class Bar{ public: //... provite: static Bar mem1;//正確,static成員可以是不完整類型 Bar* mem2;//正確,指針成員可以是不完整類型 Bar mem3;//錯誤 }
- 靜態(tài)成員可以作為默認(rèn)實參
class Screen{ public: Screen& clear(char = bkground) private: static const char bkground; }
以上是“c++中primer類怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。