溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

c++中primer類怎么用

發(fā)布時間:2021-09-27 13:33:00 來源:億速云 閱讀:141 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要介紹c++中primer類怎么用,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

類的基本思想是數(shù)據(jù)抽象和封裝。

數(shù)據(jù)抽象是依賴接口和實現(xiàn)分離的編程技術(shù)。

1. 定義抽象數(shù)據(jù)類型

1.1 設(shè)計Sales_data類

  • 成員函數(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 

1.2 定義類相關(guān)的非成員函數(shù)

  • 類相關(guān)非成員函數(shù):屬于類的接口,但是不屬于類本身。

  • 通常把函數(shù)聲明和定義分開。和類聲明在同一頭文件內(nèi)。

  • 通常情況下,拷貝一個類其實是拷貝其成員。(若想拷貝執(zhí)行其他操作,查閱拷貝賦值函數(shù))

Sale_data s1;
Sale_data s2=s1;//s2拷貝了s1的成員

1.3構(gòu)造函數(shù)

  • 構(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);
}

1.4 拷貝、賦值和析構(gòu)

  • 編譯器會為類合成拷貝、賦值和銷毀操作。

  • 編譯器生成的版本對對象的每個成員執(zhí)行拷貝、賦值和銷毀操作

2 訪問控制和封裝

  • 訪問說明符

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

2.1 友元

  • 類可以允許其他類或函數(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ù)必須在使用之前在類的外部聲明

2.2 類的其他特性

接下來介紹:類型成員、類的成員的類內(nèi)初始值、可變數(shù)據(jù)成員、內(nèi)聯(lián)成員函數(shù)、從成員函數(shù)返回*this、如何定義使用類類型、友元類

2.2.1 類成員再探
  • 類別名(類型成員):

 在類中定義的類型名字和其他成員一樣存在訪問限制,可以是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)初始值使用=的初始化形式或者花括號括起來的直接初始化形式

2.2.2 返回*this的成員函數(shù)
  • 注意返回類型是否是引用。是否是引用對函數(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ù)
}
2.2.3 類類型
  • 每個類定義了唯一的類型,即使成員完全相同,也是不一樣的類。

class A{
int member;
}
class B{
int member;
}
A a;
B b = a;//錯誤?。?/pre>
  • 不完全類型

    • 類似于函數(shù),類也可以只聲明,不定義,這被叫做不完全類型

    • 不完全類型是向程序說明這是一個類名

    • 不完全類型使用環(huán)境很有限,只是可以定義指向這種類型的指針或引用,聲明(但不能定義)以不完全類型作為參數(shù)或返回類型的函數(shù)。

  • 類在創(chuàng)建前必須被定義

  • 類的成員不能有類本身(除了后面介紹的static類),但是可以是指向自身的引用或指針

2.2.4 友元再探
  •  一個類制定了其友元類,則友元函數(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)在定義中了
};

2.4 類的作用域

  • 定義在類外的方法需要在方法名前使用::說明該方法屬于哪一個類,在說明屬于的類后,該函數(shù)的作用域位于該類內(nèi)。

    • 即返回類型使用的名字位于類的作用域之外。若返回類型也是類的成員,需要在返回類型前使用::指明返回類型屬于的類

//pos的類型聲明在window類中,并且返回類型在類的作用域外,因此要使用window::pos
window::pos window::get_pos(){
//在window::get_pos后的所有代碼作用域在類內(nèi),所以返回cursor,相當(dāng)于this->cursor
return cursor;
}
2.4.1 名字查找和類的作用域
  • 類的定義分兩步處理

    • 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;
}

2.5 構(gòu)造函數(shù)再探

2.5.1
  • 構(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;
}
  • 建議,使用初始值列表和類中變量順序一致,可能的話,盡量避免使用某些成員初始化其他成員。

2.5.2 委托構(gòu)造函數(shù)
  • 成員初始值列表的唯一入口是類名,可以用構(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ù)
}
2.5.3 默認(rèn)構(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ù)

2.5.4 隱式的類類型轉(zhuǎn)換
  • 轉(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)換
2.5.5 聚合類
  • 聚合類的定義。一個滿足下列條件的類被稱為聚合類

1.所有成員都是public的

2.沒有定義任何構(gòu)造函數(shù)

3.沒有類內(nèi)初始值

4.沒有基類,也沒有virtual函數(shù)

  • 可以使用{}括起來的成員初始值列表來初始化聚合類

class Data{
public:
	int ival;
	string s;
	}
//順序一定相同
Data val1={0,"孔子"};		
2.5.6 字面值常量
  • 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,"吃烤肉");

1.6 類的靜態(tài)成員

  • 使用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è)資訊頻道!

向AI問一下細(xì)節(jié)

免責(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)容。

AI