前情提要:
http://t.csdn.cn/Kqf6D
目錄
1.類(lèi)的6個(gè)默認(rèn)成員函數(shù)
1.1構(gòu)造函數(shù)
1.1.1?特性:
其特征如下:
1.1.2 初始化列表
注意:
1.1.3 explicit關(guān)鍵字
舉例:
1.2析構(gòu)函數(shù)
1.2.1 特性 :
析構(gòu)函數(shù)是特殊的成員函數(shù),
1.3?拷貝構(gòu)造函數(shù)
1.3.1概念:
1.3.2特征: 拷貝構(gòu)造函數(shù)也是特殊的成員函數(shù)
1.3.3特性如下:
1.4賦值運(yùn)算符重載
1.4.1運(yùn)算符重載的概念
1.4.2 賦值運(yùn)算符重載:
1. 賦值運(yùn)算符重載格式
2. 賦值運(yùn)算符只能重載成類(lèi)的成員函數(shù)不能重載成全局函數(shù)
3. 用戶(hù)沒(méi)有顯式實(shí)現(xiàn)時(shí)
1.4.3前置++和后置++重載
1.5 const成員
?編輯
1.6 取地址及const取地址操作符重載
2.初始化列表
3. static成員
3.1 概念:
3.2 特性
問(wèn)題:
4. 友元
4.1 友元函數(shù)?
友元函數(shù)的說(shuō)明
4.2 友元類(lèi)
注意:
代碼示例:
5. 內(nèi)部類(lèi)
特性:
6.拷貝對(duì)象時(shí)的一些編譯器優(yōu)化
6.1對(duì)象返回的總結(jié)
6.2函數(shù)傳參的總結(jié)
7.匿名對(duì)象
1.類(lèi)的6個(gè)默認(rèn)成員函數(shù)
如果一個(gè)類(lèi)中什么成員都沒(méi)有,簡(jiǎn)稱(chēng)為空類(lèi)。 任何類(lèi)在什么都不寫(xiě)時(shí),編譯器會(huì)自動(dòng)生成以下6個(gè)默認(rèn)成員函數(shù)。
默認(rèn)成員函數(shù):用戶(hù)沒(méi)有顯式實(shí)現(xiàn),編譯器會(huì)生成的成員函數(shù)稱(chēng)為默認(rèn)成員函數(shù)。
1.1構(gòu)造函數(shù)
構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類(lèi)名相同,創(chuàng)建類(lèi)類(lèi)型對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,以保證 每個(gè)數(shù)據(jù)成員都有 一個(gè)合適的初始值,并且在對(duì)象整個(gè)生命周期內(nèi)只調(diào)用一次。
1.1.1?特性:
構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)雖然名稱(chēng)叫構(gòu)造,但是構(gòu)造函數(shù)的主要任 務(wù)并不是開(kāi)空間創(chuàng)建對(duì)象,而是初始化對(duì)象。
其特征如下:
1. 函數(shù)名與類(lèi)名相同。
2. 無(wú)返回值。
3. 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
4. 構(gòu)造函數(shù)可以重載。
class Date
{
public:
// 1.無(wú)參構(gòu)造函數(shù)
Date()
{}
// 2.帶參構(gòu)造函數(shù)
Date(int year, int month, int day)
{
_year = year;
5. 如果類(lèi)中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù),一旦
用戶(hù)顯式定義編譯器將不再生成。
_month = month;
_day = day;
}
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),否則編譯器無(wú)法分析清楚函數(shù)聲明還是調(diào)用
// 以下代碼的函數(shù):聲明了d3函數(shù),該函數(shù)無(wú)參,返回一個(gè)日期類(lèi)型的對(duì)象
// warning C4930: “Date d3(void)”: 未調(diào)用原型函數(shù)(是否是有意用變量定義的?)
Date d3();
}
? ? ? // 注意:如果通過(guò)無(wú)參構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),對(duì)象后面不用跟括號(hào),否則編譯器無(wú)法分析清楚是函數(shù)聲明還是函數(shù)調(diào)用
? ? ? // 以下代碼的函數(shù):聲明了d3函數(shù),該函數(shù)無(wú)參,返回一個(gè)日期類(lèi)型的對(duì)象
? ? ? // warning C4930: “Date d3(void)”: 未調(diào)用原型函數(shù)(是否是有意用變量定義的?)
? ? ? Date d3();
5. 如果類(lèi)中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù),一旦用戶(hù)顯式定義編譯器將不再生成。
6. 無(wú)參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱(chēng)為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)。
注意:無(wú)參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒(méi)寫(xiě)編譯器默認(rèn)生成的構(gòu)造函數(shù),都可以認(rèn)為是默認(rèn)構(gòu)造函數(shù)。
7.編譯器生成的默認(rèn)構(gòu)造函數(shù)并沒(méi)有什么用?
C++把類(lèi)型分成內(nèi)置類(lèi)型(基本類(lèi)型)和自定義類(lèi)型。內(nèi)置類(lèi)型就是語(yǔ)言提供的數(shù)據(jù)類(lèi)型,如:int/char...,自定義類(lèi)型就是我們使用class/struct/union等自己定義的類(lèi)型,看看下面的程序,就會(huì)發(fā)現(xiàn)編譯器生成默認(rèn)的構(gòu)造函數(shù)會(huì)對(duì)自定類(lèi)型成員_t調(diào)用的它的默認(rèn)成員函數(shù)。
1.1.2 初始化列表
在創(chuàng)建對(duì)象時(shí),編譯器通過(guò)調(diào)用構(gòu)造函數(shù),給對(duì)象中各個(gè)成員變量一個(gè)合適的初始值。也就是雖然構(gòu)造函數(shù)調(diào)用之后,對(duì)象中已經(jīng)有了一個(gè)初始值,但是不能將其稱(chēng)為對(duì)對(duì)象中成員變量 的初始化,構(gòu)造函數(shù)體中的語(yǔ)句只能將其稱(chēng)為賦初值,而不能稱(chēng)作初始化。因?yàn)槌跏蓟荒艹跏?化一次,而構(gòu)造函數(shù)體內(nèi)可以多次賦值。
初始化列表:以一個(gè)冒號(hào)開(kāi)始,接著是一個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表,每個(gè)"成員變量"后面跟 一個(gè)放在括號(hào)中的初始值或表達(dá)式。
注意:
1. 每個(gè)成員變量在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次)
2. 類(lèi)中包含以下成員,必須放在初始化列表位置進(jìn)行初始化:
引用成員變量
const成員變量
自定義類(lèi)型成員(且該類(lèi)沒(méi)有默認(rèn)構(gòu)造函數(shù)時(shí))
3. 盡量使用初始化列表初始化,因?yàn)椴还苣闶欠袷褂贸跏蓟斜恚瑢?duì)于自定義類(lèi)型成員變量, 一定會(huì)先使用初始化列表初始化。
4. 成員變量在類(lèi)中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后 次序無(wú)關(guān)
1.1.3 explicit關(guān)鍵字
構(gòu)造函數(shù)不僅可以構(gòu)造與初始化對(duì)象,對(duì)于單個(gè)參數(shù)或者除第一個(gè)參數(shù)無(wú)缺省值其余均有缺省值 的構(gòu)造函數(shù),還具有類(lèi)型轉(zhuǎn)換的作用。
用explicit修飾構(gòu)造函數(shù),將會(huì)禁止構(gòu)造函數(shù)的隱式轉(zhuǎn)換。
舉例:
// 1. 單參構(gòu)造函數(shù),沒(méi)有使用explicit修飾,具有類(lèi)型轉(zhuǎn)換作用
// explicit修飾構(gòu)造函數(shù),禁止類(lèi)型轉(zhuǎn)換---explicit去掉之后,代碼可以通過(guò)編譯
class Date {
public:
//explicit Date(int year)
// :_year(year)
//{}
// 2. 雖然有多個(gè)參數(shù),但是創(chuàng)建對(duì)象時(shí)后兩個(gè)參數(shù)可以不傳遞,沒(méi)有使用explicit修飾,具
//有類(lèi)型轉(zhuǎn)換作用
// explicit修飾構(gòu)造函數(shù),禁止類(lèi)型轉(zhuǎn)換
explicit Date(int year, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1(2022);
Date d2(2023);
Date d3(d2);
// 用一個(gè)整形變量給日期類(lèi)型對(duì)象賦值
// 實(shí)際編譯器背后會(huì)用2023構(gòu)造一個(gè)無(wú)名對(duì)象,最后用無(wú)名對(duì)象給d1對(duì)象進(jìn)行賦值
d1 = 2023;
// 將1屏蔽掉,2放開(kāi)時(shí)則編譯失敗,因?yàn)閑xplicit修飾構(gòu)造函數(shù),禁止了單參構(gòu)造函數(shù)類(lèi)型轉(zhuǎn)換的作用
}
int main()
{
Test();
}
1.2析構(gòu)函數(shù)
? 析構(gòu)函數(shù)與構(gòu)造函數(shù)功能相反,析構(gòu)函數(shù)不是完成對(duì)對(duì)象本身的銷(xiāo)毀,局部對(duì)象銷(xiāo)毀工作是由 編譯器完成的。而對(duì)象在銷(xiāo)毀時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對(duì)象中資源的清理工作。
1.2.1 特性 :
析構(gòu)函數(shù)是特殊的成員函數(shù),
1.2.2其特征如下:
1. 析構(gòu)函數(shù)名是在類(lèi)名前加上字符 ~。
2. 無(wú)參數(shù)無(wú)返回值類(lèi)型。
3. 一個(gè)類(lèi)只能有一個(gè)析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。注意:析構(gòu)函數(shù)不能重載
4. 對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)。
5.?
內(nèi)置類(lèi)型成員:銷(xiāo)毀時(shí)不需要資源清理,最后系統(tǒng)直接將其內(nèi)存回收即可;
自定義類(lèi)型而:若一個(gè)類(lèi)的對(duì)象內(nèi)部也有對(duì)象,要將其內(nèi)部包含的類(lèi)的對(duì)象銷(xiāo)毀,所以要調(diào)用這個(gè)類(lèi)對(duì)應(yīng)的析構(gòu)函數(shù)。
6. 如果類(lèi)中沒(méi)有申請(qǐng)資源時(shí),析構(gòu)函數(shù)可以不寫(xiě),直接使用編譯器生成的默認(rèn)析構(gòu)函數(shù),比如 Date類(lèi);有資源申請(qǐng)時(shí),一定要寫(xiě),否則會(huì)造成資源泄漏,比如Stack類(lèi)。
1.3?拷貝構(gòu)造函數(shù)
1.3.1概念:
只有單個(gè)形參,該形參是對(duì)本類(lèi)類(lèi)型對(duì)象的引用(一般常用const修飾),在用已存在的類(lèi)類(lèi)型對(duì)象創(chuàng)建新對(duì)象時(shí)由編譯器自動(dòng)調(diào)用。
1.3.2特征: 拷貝構(gòu)造函數(shù)也是特殊的成員函數(shù)
1.3.3特性如下:
1. 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式。
2. 拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須是類(lèi)類(lèi)型對(duì)象的引用,使用傳值方式編譯器直接報(bào)錯(cuò), 因?yàn)闀?huì)引發(fā)無(wú)窮遞歸調(diào)用。
3. 若未顯式定義,編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。 默認(rèn)的拷貝構(gòu)造函數(shù)對(duì)象按內(nèi)存存儲(chǔ)按 字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝。
#include<stdlib.h>
#include<iostream>
class Date
{
private:
// 基本類(lèi)型(內(nèi)置類(lèi)型)
int _year = 2023;
int _month = 4;
int _day = 4;
// 自定義類(lèi)型
Time _t;
};
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
// 注意:在編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)中,內(nèi)置類(lèi)型是按照字節(jié)方式直接拷貝的,而自定
// 義類(lèi)型是調(diào)用其拷貝構(gòu)造函數(shù)完成拷貝的。
}
Time(const Time& t)
{
_hour = t._hour+1;
_minute = t._minute+1;
_second = t._second+1;
std::cout << "Time::Time(const Time&)" << std::endl;
}
private:
int _hour;
int _minute;
int _second;
};
int main()
{
Date d1;//構(gòu)造函數(shù)
// 用已經(jīng)存在的d1拷貝構(gòu)造d2,此處會(huì)調(diào)用Date類(lèi)的拷貝構(gòu)造函數(shù)
// 但Date類(lèi)并沒(méi)有顯式定義拷貝構(gòu)造函數(shù),則編譯器會(huì)給Date類(lèi)生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)
Date d2(d1);
return 0;
}
?其實(shí)此處Time生成的默認(rèn)拷貝構(gòu)造函數(shù)也是能使用的
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
//Time(const Time& t)
//{
// _hour = t._hour+1;
// _minute = t._minute+1;
// _second = t._second+1;
// std::cout << "Time::Time(const Time&)" << std::endl;
//}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本類(lèi)型(內(nèi)置類(lèi)型)
int _year = 2023;
int _month = 4;
int _day = 4;
// 自定義類(lèi)型
Time _t;
};
int main()
{
Date d1;//構(gòu)造函數(shù)
// 用已經(jīng)存在的d1拷貝構(gòu)造d2,此處會(huì)調(diào)用Date類(lèi)的拷貝構(gòu)造函數(shù)
// 但Date類(lèi)并沒(méi)有顯式定義拷貝構(gòu)造函數(shù),則編譯器會(huì)給Date類(lèi)生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)
Date d2(d1);
return 0;
}
4.類(lèi)中如果沒(méi)有涉及資源申請(qǐng)時(shí),拷貝構(gòu)造函數(shù)是否寫(xiě)都可以;一旦涉及到資源申請(qǐng) 時(shí),則拷貝構(gòu)造函數(shù)是一定要寫(xiě)的,否則就是淺拷貝。
5. 拷貝構(gòu)造函數(shù)典型調(diào)用場(chǎng)景:
1.使用已存在對(duì)象創(chuàng)建新對(duì)象
2.函數(shù)參數(shù)類(lèi)型為類(lèi)類(lèi)型對(duì)象
3.函數(shù)返回值類(lèi)型為類(lèi)類(lèi)型對(duì)象
1.4賦值運(yùn)算符重載
1.4.1運(yù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ù)列表)
運(yùn)算符重載成全局函數(shù),注意重載成全局函數(shù)時(shí)沒(méi)有this指針了,需要給兩個(gè)參數(shù)。
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
但是這是全局的operator==,如何保證其封裝性呢?
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// bool operator==(Date* this, const Date& d2)
// 這里需要注意的是,左操作數(shù)是this,指向調(diào)用函數(shù)的對(duì)象
bool operator==(const Date& d2)
{
return _year == d2._year;
&& _month == d2._month
&& _day == d2._day;
}
private:
int _year;
int _month;
int _day;
};
注意:
1.不能通過(guò)連接其他符號(hào)來(lái)創(chuàng)建新的操作符:比如operator@。
2.重載操作符必須有一個(gè)類(lèi)類(lèi)型參數(shù) 用于內(nèi)置類(lèi)型的運(yùn)算符,其含義不能改變。
例如:內(nèi)置的整型+,不能改變其含義作為類(lèi)成員函數(shù)重載時(shí),其形參看起來(lái)比操作數(shù)數(shù)目少1,因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱 藏的this? .*? ::? sizeof? ?:? . 注意以上5個(gè)運(yùn)算符不能重載。
1.4.2 賦值運(yùn)算符重載:
1. 賦值運(yùn)算符重載格式
參數(shù)類(lèi)型:const T&,傳遞引用可以提高傳參效率
返回值類(lèi)型:T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續(xù)賦值
檢測(cè)是否自己給自己賦值:*this!=d1;
返回*this :要復(fù)合連續(xù)賦值的含義
2. 賦值運(yùn)算符只能重載成類(lèi)的成員函數(shù)不能重載成全局函數(shù)
原因:賦值運(yùn)算符如果不顯式實(shí)現(xiàn),編譯器會(huì)生成一個(gè)默認(rèn)的。此時(shí)用戶(hù)再在類(lèi)外自己實(shí)現(xiàn) 一個(gè)全局的賦值運(yùn)算符重載,就和編譯器在類(lèi)中生成的默認(rèn)賦值運(yùn)算符重載沖突了,故賦值 運(yùn)算符重載只能是類(lèi)的成員函數(shù)。
3. 用戶(hù)沒(méi)有顯式實(shí)現(xiàn)時(shí)
編譯器會(huì)生成一個(gè)默認(rèn)賦值運(yùn)算符重載,以值的方式逐字節(jié)拷貝。
注意:
1.內(nèi)置類(lèi)型成員變量是直接賦值的,而自定義類(lèi)型成員變量需要調(diào)用對(duì)應(yīng)類(lèi)的賦值運(yùn)算符重載完成賦值。
2.如果類(lèi)中未涉及到資源管理,賦值運(yùn)算符是否實(shí)現(xiàn)都可以;一旦涉及到資源管理則必須要實(shí)現(xiàn)。
1.4.3前置++和后置++重載
1. 前置++:返回+1之后的結(jié)果
?注意:this指向的對(duì)象函數(shù)結(jié)束后不會(huì)銷(xiāo)毀,故以引用方式返回提高效率
Date& operator++()
{
_day += 1;
return *this;
}
?2.后置++
前置++和后置++都是一元運(yùn)算符,為了讓前置++與后置++形成能正確重載
C++規(guī)定:后置++重載時(shí)多增加一個(gè)int類(lèi)型的參數(shù),但調(diào)用函數(shù)時(shí)該參數(shù)不用傳遞,編譯器
自動(dòng)傳遞
?注意:后置++是先使用后+1,因此需要返回+1之前的舊值,故需在實(shí)現(xiàn)時(shí)需要先將this保存
一份,然后給this+1
?而temp是臨時(shí)對(duì)象,因此只能以值的方式返回,不能返回引用
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
1.5 const成員
將const修飾的“成員函數(shù)”稱(chēng)之為const成員函數(shù),const修飾類(lèi)成員函數(shù),實(shí)際修飾該成員函數(shù) 隱含的this指針,表明在該成員函數(shù)中不能對(duì)類(lèi)的任何成員進(jìn)行修改。
1.6 取地址及const取地址操作符重載
這兩個(gè)默認(rèn)成員函數(shù)一般不用重新定義 ,編譯器默認(rèn)會(huì)生成。
這兩個(gè)運(yùn)算符一般不需要重載,使用編譯器生成的默認(rèn)取地址的重載即可,只有特殊情況,才需 要重載,比如想讓別人獲取到指定的內(nèi)容!
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
2.初始化列表
初始化列表:以一個(gè)冒號(hào)開(kāi)始,接著是一個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表,每個(gè)"成員變量"后面跟 一個(gè)放在括號(hào)中的初始值或表達(dá)式。
Date(int year, int month, int day)
: _year(2023)
, _month(4)
, _day(4)
{}
注意:
1. 每個(gè)成員變量在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次)
2. 類(lèi)中包含以下成員,必須放在初始化列表位置進(jìn)行初始化:
引用成員變量? ?const成員變量? ? 自定義類(lèi)型成員(且該類(lèi)沒(méi)有默認(rèn)構(gòu)造函數(shù)時(shí))
3.盡量使用初始化列表初始化,因?yàn)椴还苣闶欠袷褂贸跏蓟斜?,?duì)于自定義類(lèi)型成員變量, 一定會(huì)先使用初始化列表初始化。
4. 成員變量在類(lèi)中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后 次序無(wú)關(guān)
2.1 explicit關(guān)鍵字
構(gòu)造函數(shù)不僅可以構(gòu)造與初始化對(duì)象,對(duì)于單個(gè)參數(shù)或者除第一個(gè)參數(shù)無(wú)默認(rèn)值其余均有默認(rèn)值 的構(gòu)造函數(shù),還具有類(lèi)型轉(zhuǎn)換的作用。
以下是C++11規(guī)定的多參數(shù)構(gòu)造函數(shù)的隱式類(lèi)型轉(zhuǎn)換的舉例
單參構(gòu)造函數(shù)同理
using namespace std;
class A {
public:
A(int a, int b,int c)
: _year ( a),
_month (b),
_day (c)
{
}
A(const A& a)
{
cout << "const A& " << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
A a = {1,2,3};
}
1. 單參/多參 構(gòu)造函數(shù),沒(méi)有使用explicit修飾,具有類(lèi)型轉(zhuǎn)換作用
?explicit修飾構(gòu)造函數(shù),禁止類(lèi)型轉(zhuǎn)換
3. static成員
3.1 概念:
聲明為static的類(lèi)成員稱(chēng)為類(lèi)的靜態(tài)成員,用static修飾的成員變量,稱(chēng)之為靜態(tài)成員變量;用 static修飾的成員函數(shù),稱(chēng)之為靜態(tài)成員函數(shù)。靜態(tài)成員變量一定要在類(lèi)外進(jìn)行初始化 面試題:實(shí)現(xiàn)一個(gè)類(lèi),計(jì)算程序中創(chuàng)建出了多少個(gè)類(lèi)對(duì)象。
3.2 特性
1. 靜態(tài)成員為所有類(lèi)對(duì)象所共享,不屬于某個(gè)具體的對(duì)象,存放在靜態(tài)區(qū)
2. 靜態(tài)成員變量必須在類(lèi)外定義,定義時(shí)不添加static關(guān)鍵字,類(lèi)中只是聲明
3. 類(lèi)靜態(tài)成員即可用 類(lèi)名::靜態(tài)成員 或者 對(duì)象.靜態(tài)成員 來(lái)訪問(wèn)
4. 靜態(tài)成員函數(shù)沒(méi)有隱藏的this指針,不能訪問(wèn)任何非靜態(tài)成員
5. 靜態(tài)成員也是類(lèi)的成員,受public、protected、private 訪問(wèn)限定符的限定
問(wèn)題:
1. 靜態(tài)成員函數(shù)可以調(diào)用非靜態(tài)成員函數(shù)嗎?不可以
2. 非靜態(tài)成員函數(shù)可以調(diào)用類(lèi)的靜態(tài)成員函數(shù)嗎?可以
例題:求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等關(guān)鍵字及條件判斷語(yǔ)句(A?B:C)。
使用靜態(tài)成員變量
using namespace std;
class Sum
{
public:
Sum()
{
_sum += _n;
++_n;
}
static int GetSum()
{
return _sum;
}
private:
static int _n;
static int _sum;
};
class Solution {
public:
int Sum_Solution(int n) {
Sum* p = new Sum[n];
return Sum::GetSum();
}
~Solution()
{
cout << "~Solution()" << endl;
}
};
int Sum::_n = 1;
int Sum::_sum = 0;
int main()
{
cout << Solution().Sum_Solution(100) << endl;
}
不使用靜態(tài)成員變量
class Solution {
public:
int Sum_Solution(int n) {
//通過(guò)與運(yùn)算判斷n是否為正數(shù),以結(jié)束遞歸
n && (n += Sum_Solution(n - 1));
return n;
}
};
4. 友元
友元提供了一種突破封裝的方式,有時(shí)提供了便利。但是友元會(huì)增加耦合度,破壞了封裝,所以 友元不宜多用。 友元分為:友元函數(shù)和友元類(lèi)
4.1 友元函數(shù)?
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常規(guī)調(diào)用
// 因?yàn)槌蓡T函數(shù)第一個(gè)參數(shù)一定是隱藏的this,所以d1必須放在<<的左側(cè)
ostream& operator<<(ostream& _cout)
{
_cout << _year << "-" << _month << "-" << _day << endl;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
友元函數(shù)可以直接訪問(wèn)類(lèi)的私有成員,它是定義在類(lèi)外部的普通函數(shù),不屬于任何類(lèi),但需要在 類(lèi)的內(nèi)部聲明,聲明時(shí)需要加friend關(guān)鍵字。
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}
友元函數(shù)的說(shuō)明
友元函數(shù)可訪問(wèn)類(lèi)的私有和保護(hù)成員,但不是類(lèi)的成員函數(shù) 友元函數(shù)不能用const修飾
友元函數(shù)可以在類(lèi)定義的任何地方聲明,不受類(lèi)訪問(wèn)限定符限制
一個(gè)函數(shù)可以是多個(gè)類(lèi)的友元函數(shù)
友元函數(shù)的調(diào)用與普通函數(shù)的調(diào)用原理相同
4.2 友元類(lèi)
友元類(lèi)的所有成員函數(shù)都可以是另一個(gè)類(lèi)的友元函數(shù),都可以訪問(wèn)另一個(gè)類(lèi)中的非公有成員。
注意:
友元關(guān)系是單向的,不具有交換性。
比如下面代碼示例中的Time類(lèi)和Date類(lèi),在Time類(lèi)中聲明Date類(lèi)為其友元類(lèi),那么可以在Date類(lèi)中直接 訪問(wèn)Time類(lèi)的私有成員變量,但想在Time類(lèi)中訪問(wèn)Date類(lèi)中私有的成員變量則不行。
友元關(guān)系不能傳遞 如果C是B的友元, B是A的友元,則不能說(shuō)明C時(shí)A的友元。
友元關(guān)系不能繼承,在繼承位置再給大家詳細(xì)介紹。
代碼示例:
class Time
{
friend class Date; // 聲明日期類(lèi)為時(shí)間類(lèi)的友元類(lèi),則在日期類(lèi)中就直接訪問(wèn)Time類(lèi)中的私有成員變量
public:
Time(int hour = 0, int minute = 0, int second = 0)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接訪問(wèn)時(shí)間類(lèi)私有的成員變量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
5. 內(nèi)部類(lèi)
概念:如果一個(gè)類(lèi)定義在另一個(gè)類(lèi)的內(nèi)部,這個(gè)內(nèi)部類(lèi)就叫做內(nèi)部類(lèi)。內(nèi)部類(lèi)是一個(gè)獨(dú)立的類(lèi), 它不屬于外部類(lèi),更不能通過(guò)外部類(lèi)的對(duì)象去訪問(wèn)內(nèi)部類(lèi)的成員。外部類(lèi)對(duì)內(nèi)部類(lèi)沒(méi)有任何優(yōu)越 的訪問(wèn)權(quán)限。
注意:內(nèi)部類(lèi)就是外部類(lèi)的友元類(lèi),參見(jiàn)友元類(lèi)的定義,內(nèi)部類(lèi)可以通過(guò)外部類(lèi)的對(duì)象參數(shù)來(lái)訪 問(wèn)外部類(lèi)中的所有成員。但是外部類(lèi)不是內(nèi)部類(lèi)的友元。
特性:
1. 內(nèi)部類(lèi)可以定義在外部類(lèi)的public、protected、private都是可以的。
2. 注意內(nèi)部類(lèi)可以直接訪問(wèn)外部類(lèi)中的static成員,不需要外部類(lèi)的對(duì)象/類(lèi)名。
3. sizeof(外部類(lèi))=外部類(lèi),和內(nèi)部類(lèi)沒(méi)有任何關(guān)系。
對(duì)上面例題進(jìn)行內(nèi)部類(lèi)封裝
class Solution {
public:
class Sum {
public:
Sum() {
_sum += _n;
++_n;
}
static int GetSum() {
return _sum;
}
};
int Sum_Solution(int n) {
Sum* p = new Sum[n];
return Sum::GetSum();
}
~Solution() {
cout << "~Solution()" << endl;
}
private:
static int _n;
static int _sum;
};
int Solution::_n = 1;
int Solution::_sum = 0;
6.拷貝對(duì)象時(shí)的一些編譯器優(yōu)化
在傳參和傳返回值的過(guò)程中,一般編譯器會(huì)做一些優(yōu)化,減少對(duì)象的拷貝,這個(gè)在一些場(chǎng)景下還 是非常有用的。
6.1對(duì)象返回的總結(jié)
1.接受返回值的對(duì)象,盡量使用拷貝構(gòu)造方式接收,不要賦值接收。
2.函數(shù)中返回對(duì)象時(shí)盡量返回匿名對(duì)象。
6.2函數(shù)傳參的總結(jié)
盡量使用const &傳參。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-407903.html
7.匿名對(duì)象
但是我們可以這么定義匿名對(duì)象,匿名對(duì)象的特點(diǎn)不用取名字, 但是他的生命周期只有這一行文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-407903.html
cout << Solution().Sum_Solution(100) << endl;
到了這里,關(guān)于近萬(wàn)字的超詳細(xì)C++類(lèi)和對(duì)象,快進(jìn)來(lái)看看吧的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!