這一節(jié)中主要講了對象和函數(shù)在使用和調(diào)用過程中一些注意事項(xiàng),比較重要的是右值引用和最后的move和forward
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、重慶小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了班瑪免費(fèi)建站歡迎大家使用!
對于以下這個(gè)測試類,列出了十幾種不同的定義方式
class Test {
public:
Test(int a = 4, int b = 10) : ma(a), mb(b) {
cout << "Test()" << endl;
}
~Test() {
cout << "~Test()" << endl;
}
Test(const Test &src) {
ma = src.ma;
mb = src.mb;
cout << "Test(const Test&)" << endl;
}
Test &operator=(const Test &src) {
ma = src.ma;
mb = src.mb;
cout << "operator=(const Test&)" << endl;
return *this;
}
private:
int ma;
int mb;
};
實(shí)現(xiàn)結(jié)果如下:
有幾個(gè)比較值得注意的點(diǎn):
t2=60
,編譯器會找對象中有無合適的構(gòu)造方法生成對象函數(shù)調(diào)用的過程中,實(shí)參傳遞到形參需要重新初始化,函數(shù)的形參對象需要初始化,這個(gè)過程中會調(diào)用對象的拷貝構(gòu)造方法。
函數(shù)體內(nèi)部返回的對象也要現(xiàn)在main棧幀中拷貝構(gòu)造一個(gè)臨時(shí)變量,才能在main作用域中訪問這個(gè)對象。
函數(shù)體執(zhí)行完畢后需要先析構(gòu)函數(shù)體內(nèi)構(gòu)造的對象,然后再析構(gòu)形參列表構(gòu)造的對象
上圖中的代碼最后被優(yōu)化為以下代碼:
Test GetObject(Test &t){
int val=t.getData();
return Test(val);//定義臨時(shí)對象 2.Test()
}
int main(){
Test t1;//1.Test()
Test t2=GetObject(t1);//用臨時(shí)對象拷貝構(gòu)造同類型的新對象,編譯器會優(yōu)化此過程 少了臨時(shí)對象在main棧幀上的構(gòu)造和析構(gòu)
return 0;
}
//3.~Test()
//4.~Test()
優(yōu)化完只剩下4步構(gòu)造析構(gòu)的過程
class String {
friend std::ostream &operator<<(std::ostream &os, const String &src);
friend String operator+(const String &l, const String &r);
public:
String(const char *src = nullptr) {
if (src == nullptr) {
_pstr = new char[1];
*_pstr = '\0';
} else {
_pstr = new char[strlen(src) + 1];
strcpy(_pstr, src);
}
std::cout<<"String(const char *src = nullptr)"<(const String &str) const {
return strcmp(_pstr, str._pstr) > 0;
}
bool operator<(const String &str) const {
return strcmp(_pstr, str._pstr) < 0;
}
bool operator==(const String &str) const {
return strcmp(_pstr, str._pstr) == 0;
}
int length() const {
return strlen(_pstr);
}
char &operator[](int index) {
return _pstr[index];
}
char *c_str() const {
return _pstr;
}
private:
char *_pstr;
};
String GetString(String& str){
const char* pstr=str.c_str();
String tmpStr(pstr);
return tmpStr;//這一步要在main棧幀中拷貝構(gòu)造一個(gè)臨時(shí)變量,會重新劃分一塊內(nèi)存
}
int main(){
String s1("assf");
String s2;
s2=GetString(s1);//調(diào)用賦值重載函數(shù),會刪除原有內(nèi)存,重新劃分一塊內(nèi)存
cout<
在調(diào)用中出現(xiàn)了多次臨時(shí)對象,產(chǎn)生一個(gè)臨時(shí)對象就要在棧幀上拷貝賦值原來的內(nèi)存,而使用一次就要?jiǎng)h除,非常耗時(shí)
一個(gè)右值引用變量本身是一個(gè)左值,所以一個(gè)定義好的右值引用變量不能賦值給右值引用
帶右值引用參數(shù)的拷貝構(gòu)造函數(shù)和賦值重載函數(shù)會指向臨時(shí)對象開辟的內(nèi)存,在整個(gè)過程中不會有無效的內(nèi)存釋放和開辟,大幅提高了運(yùn)行效率
實(shí)例代碼如下:
//帶右值引用的拷貝構(gòu)造函數(shù)
String(String &&src) noexcept {
std::cout<<"String(String &&)"<
輸出結(jié)果如下:
帶有左值引用的拷貝重載的對象中一般都有帶右值引用的拷貝重載的版本。
在push_back的過程中,如果傳入左值,匹配帶有左值參數(shù)的臨時(shí)對象,如果傳入臨時(shí)對象,會首先調(diào)用臨時(shí)對象的構(gòu)造函數(shù),再調(diào)用帶右值參數(shù)的拷貝構(gòu)造函數(shù)。
為什么push_back會調(diào)用帶有右值引用的拷貝構(gòu)造函數(shù)?看下面 \(\Downarrow\)
move()是將左值轉(zhuǎn)化為右值
forward()是指類型的完美轉(zhuǎn)發(fā),能夠識別左值和右值類型
如果在自己定義的vector類里定義支持調(diào)用右值引用的push_back方法,首先要push_back的參數(shù)是一個(gè)右值引用的類型
第一種寫法:使用函數(shù)重載,分別定義一個(gè)參數(shù)是左值引用的和一個(gè)參數(shù)是右值引用的函數(shù)
void push_back(T &val) {
if (full()) {
expend();
}
//*_last++ = val;
_alloctor.construct(_last, val);
_last++;
}
void push_back(T &&val) {
if (full()) {
expend();
}
_alloctor.construct(_last, std::move(val));
_last++;
}
在函數(shù)鐘調(diào)用了_alloctor.construct()
,該函數(shù)傳遞了參數(shù)val,所以也需要函數(shù)重載接受右值引用和左值引用。
void construct(T *p, const T &val) {//負(fù)責(zé)對象構(gòu)造
new(p) T(val);//定位new
}
void construct(T *p, const T &&val) {//負(fù)責(zé)對象構(gòu)造
new(p) T(std::move(val));//定位new
}
第二種寫法:使用函數(shù)模板的類型推演和引用折疊
首先說明引用折疊是什么意思。如果函數(shù)模板推演出的類型是Ty&& + &&(+后面的&&是參數(shù)中,屬于必帶的符號),引用折疊后的類型就就是Ty&&,是右值引用;如果函數(shù)模板推演出的類型是Ty& + &&,引用折疊后的類型就就是Ty&,是左值引用。使用forward可以識別Ty的左值或者右值的類型。
template
void push_back(Ty &&val) {//Ty識別傳入?yún)?shù)是左值還是右值,然后進(jìn)行引用折疊
if (full()) {
expend();
}
_alloctor.construct(_last, std::forward(val));//將val轉(zhuǎn)換為Ty識別到的類型,避免使用函數(shù)重載
_last++;
}
template
void construct(T *p, Ty &&val) {//Ty識別傳入?yún)?shù)是左值還是右值,然后進(jìn)行引用折疊
new(p) T(std::forward(val));//將val轉(zhuǎn)換為Ty識別到的類型,避免使用函數(shù)重載
}