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