C++:類和對象4

一,日期類實現

學習建議:

對于計算機學習來說,調試十分重要,所以在日常學習中一定要加大代碼練習,刷代碼題和課后自己敲出課上代碼例題,注意不要去對比正確代碼或者網上找正確代碼直接使用,一定要自己去調試更改。

調試技巧:1,通過不斷改變運算對象改變調試范圍(學會縮小和放大范圍),判斷可能出現錯誤的程序范圍

? ? ? ? ? ? ? ? ? ?2,當不知道問題出現在哪的時候,對于一個對象用監視一步一步走,看該對象的變化,再對比自己的計算,判斷出那塊代碼出現問題

還有很多調試技巧需要自己去理解總結

Date.h

#pragma once#include<iostream>
using namespace std;
#include<assert.h>class Date
{// 友元函數聲明 friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 直接定義類??,他默認是inline // 只有頻繁調?的小函數在h文件中定義//得到月份所對應的天數int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))//閏年的二月份是20天的判斷+返回{// 2return 29;}else{return monthDayArray[month];}}//d1+=天數Date& operator+=(int day);Date operator+(int day);//d1-=天數Date& operator-=(int day);Date operator-(int day);// ++d1 ->d1.operator++()  前置++Date& operator++();// d1++ ->d1.operator++(1) 后置++  為了區分,構成重載,給后置++,強?增加了?個int形參 // 這?不需要寫形參名,因為接收值是多少不重要,也不需要? // 這個參數僅僅是為了跟前置++構成重載區分 Date operator++(int);//--d1 前置--Date& operator--();//d1-- 后置--Date operator--(int);//大于 大于等于 小于 小于等于 等于 不等于bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator==(const Date& d);bool operator!=(const Date& d);// d1-d2int operator-(const Date& d); // 流插? // 不建議,因為Date* this占據了?個參數位置,使?d<<cout不符合習慣 //void operator<<(ostream& out);//打印void Print();
private:int _year;int _month;int _day;
};//重載
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

Date.cpp

#include"Date.h"bool Date::CheckDate()//檢查日期是否正確·
{if (_month < 1 || _month > 12|| _day < 1 || _day > GetMonthDay(_year, _month)){return false;//天數,月數,年數不對時直接報錯}else{return true;}
}Date::Date(int year, int month, int day)//對日期賦值
{_year = year;_month = month;_day = day;if (!CheckDate()){cout << "?期?法" << endl;}
}void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}// d1 < d2
bool Date::operator<(const Date& d) 
{if (_year < d._year)//先對年份進行判斷{return true;}else if (_year == d._year){if (_month < d._month)//年份相同再對月份進行判斷{return true;}else if (_month == d._month)//月份相同時則d1<d2{return _day < d._day;}}return false;
}// d1 <= d2
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;//直接借用上述的 < 運算符重載
}//同理
//d1>d2
bool Date::operator>(const Date& d) 
{return !(*this <= d);//直接利用上述小于等于運算符取逆
}//d1>=d2
bool Date::operator>=(const Date& d) 
{return !(*this < d);
}//d1==d2
bool Date::operator==(const Date& d) 
{return _year == d._year&& _month == d._month&& _day == d._day;
}//d1!=d2
bool Date::operator!=(const Date& d) const
{return !(*this == d);
}//d1+=天數
Date& Date::operator+=(int day)//重載運算函數是成員函數,,第一個參數是this指針,參數比運算對象少一個
{if (day < 0)// 當要加的數為負數時要求借用減法完成{return *this -= -day;}_day += day;//完成加法運算while (_day > GetMonthDay(_year, _month))//天數超越了對應月份的天數{_day -= GetMonthDay(_year, _month);//天數減去對應月份的天數++_month;//同時月份跳轉到下一個月if (_month == 13)//當月份跳轉到13時進入下一年,同時月份重新定義為1{_year++;_month = 1;}}return *this;//返回第一個運算對象
}//d1+天數
Date Date::operator+(int day)//完成加法運算
{Date tmp(*this);//拷貝一份*this用拷貝的tmp完成+=運算tmp += day;return tmp;
}//d1-=天數
Date& Date::operator-=(int day)//同理,重載運算函數是成員函數
{if (day < 0)//減去的數為負數的情況{return *this += -day;}_day -= day;//減法運算while (_day <= 0)//天數小于等于0{--_month;//月份-1if (_month = 0)//當月份為0時,跳轉到上一年,同時月份重新定義為12{_year--;_month = 12;}_day += GetMonthDay(_year, _month); //  不斷借助上一個月天數,使得負_day絕對值變小,直至不滿足循環條件 }return *this;//返回自身
}//d1-天數
Date Date::operator-(int day)
{Date tmp(*this);//自身不能改變,拷貝,借助拷貝對象改變返回tmp -= day;return tmp;
}//++d1
Date& Date::operator++()
{*this += 1;return *this;
}//d1++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}//--d1
Date& Date::operator--()
{*this -= 1;return *this;
}//d1--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}//d1-d2
int Date::operator-(const Date& d) 
{int flag = 1;//假設法Date max = *this;//假設第一個大,第二個小Date min = d;if (*this < d)//假設錯了就交換{max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;//借助n計算相差個數}return n * flag;//flag就是在兩個數大小不同情況下確定運算是正還是負
}ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "?" << d._day << "?" << endl;return out;
}istream& operator>>(istream& in, Date& d)
{cout << "請依次輸?年??:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "?期?法" << endl;}return in;
}

d1-d2的實現思路:

思路一:

1,小的月和天,朝大的對齊,算出來差了多少天

2,年直接減,算出來差多少天

例子:2025.3.11? 和2020.5.1

先將3.11向5.1對齊發現三月十一到五月一號差了17+30+1天,此時為2025.5.1和2020.5.1相差五年再看其中閏年,從而計算相差天數再減去48天

思路二:直接賦用

將小的日期不斷++直到與大的一致,算出來++了多少次。

上述代碼就是借助思路二實現的

Test.cpp

#include"Date.h"
void TestDate1()
{// 這?需要測試?下?的數據+和- Date d1(2024, 4, 14);Date d2 = d1 + 30000;d1.Print();d2.Print();Date d3(2024, 4, 14);Date d4 = d3 - 5000;d3.Print();d4.Print();Date d5(2024, 4, 14);d5 += -5000;d5.Print();
}void TestDate2()
{Date d1(2024, 4, 14);Date d2 = ++d1;d1.Print();d2.Print();Date d3 = d1++;d1.Print();d3.Print();d1.operator++(1);d1.operator++(100);d1.operator++(0);d1.Print();
}void TestDate3()
{Date d1(2024, 4, 14);Date d2(2034, 4, 14);int n = d1 - d2;cout << n << endl;n = d2 - d1;
}void TestDate4()
{Date d1(2024, 4, 14);Date d2 = d1 + 30000;// operator<<(cout, d1)cout << d1;cout << d2;cin >> d1 >> d2;cout << d1 << d2;
}
void TestDate5()
{const Date d1(2024, 4, 14);d1.Print();//d1 += 100;d1 + 100;Date d2(2024, 4, 25);d2.Print();d2 += 100;d1 < d2;d2 < d1;
}int main()
{return 0;
}

二,取地址運算符重載

1,const成員函數

(1)將const修飾的成員函數稱之為const成員函數,const修飾成員函數放到成員函數參數列表的后 ?。

(2)const實際修飾該成員函數隱含的this指針,表明在該成員函數中不能對類的任何成員進?修改。 const修飾Date類的Print成員函數,Print隱含的this指針由 Date* const this 變為 const Date* const this

在上述的成員函數后面都可以加上const。

下面例子出現權限放大:調用print函數需要傳遞this指針,也就是&d1,而this指針這里相當于Date const this

而上面的const修飾的相當于const Date*,傳遞過去時會發生權限放大,報錯

例子2:

const Date*傳遞過去會發生權限的平移

Date*傳遞過去發生權限縮小

2,取地址運算符重載

取地址運算符重載分為普通取地址運算符重載和const取地址運算符重載,?般這兩個函數編譯器?動 ?成的就可以夠我們?了,不需要去顯?實現。除??些很特殊的場景,?如我們不想讓別?取到當前類對象的地址,就可以??實現?份,胡亂返回?個地址。

class Date
{ 
public :Date* operator&(){return this;// return nullptr;//當不想讓別人取到地址,可以返回nullptr或者return (Date*)0x112245EF 返回一個假地址}const Date* operator&()const{return this;// return nullptr;}
private :int _year ; // 年 int _month ; // ? int _day ; // ? 
};

三,再探構造函數

(1)之前我們實現構造函數時,初始化成員變量主要使?函數體內賦值,構造函數初始化還有?種? 式,就是初始化列表,初始化列表的使??式是以?個冒號開始,接著是?個以逗號分隔的數據成 員列表,每個"成員變量"后?跟?個放在括號中的初始值或表達式

class Date
{
public://初始化列表Date(int& x, int year = 1, int month = 1, int day = 1):_year(year), _month(month), _n(10),_ref(x)
//可以混合使用兩者初始化方法{_day = day;_n=1;//不可行,初始化列表的變量只能初始化一次}private:int _year;int _month;int _day;// 必須在初始化列表初始化的成員const int _n;//常量初始化int& _ref;//引用初始化};

(2)每個成員變量在初始化列表中只能出現?次,語法理解上初始化列表可以認為是每個成員變量定義 初始化的地?。

(3)引?成員變量,const成員變量,沒有默認構造的類類型變量,必須放在初始化列表位置進?初始 化,否則會編譯報錯。

class Date
{
public:Date(int& x, int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day),_t(12),_ref(x),_n(1){// error C2512: “Time”: 沒有合適的默認構造函數可? // error C2530 : “Date::_ref” : 必須初始化引? // error C2789 : “Date::_n” : 必須初始化常量限定類型的對象 }void Print() const
{cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;Time _t; // 沒有默認構造 int& _ref; // 引? const int _n; // const 
};

(4)C++11?持在成員變量聲明的位置給缺省值,這個缺省值主要是給沒有顯?在初始化列表初始化的 成員使?的。

class Time
{
public:Time(int hour):_hour(hour){cout << "Time()" << endl;}
private:int _hour;
};class Date
{
public:Date():_month(2){cout << "Date()" << endl;}void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}private:// 注意這里不是初始化,這里給的是缺省值,這個缺省值是給初始化列表的// 如果初始化列表沒有顯示初始化,默認就會用這個缺省值初始化int _year = 1;//year沒有在列表中初始化,但是通過了缺省值進行初始化。int _month = 1;int _day;const int _n = 1;Time _t = 10;int* _ptr = (int*)malloc(12);//缺省值也可以是一個表達式,其實只要是一個值就行
};class Stack
{
public:Stack(int n = 4){cout << "Stack(int n)" << endl;}
private://...
};class MyQueue
{
public:MyQueue(int n);_pushst(n),popost(n){}private:// 聲明Stack _pushst;//自定義類型沒有默認構造,需要顯示的寫構造函數Stack _popst;const int _n = 1; // 缺省值初始化
};
int main()
{Date d1;MyQueue q;//調用初始化構造函數return 0;
}

(5)盡量使?初始化列表初始化,因為那些你不在初始化列表初始化的成員也會?初始化列表,如果這 個成員在聲明位置給了缺省值,初始化列表會?這個缺省值初始化。如果你沒有給缺省值,對于沒 有顯?在初始化列表初始化的內置類型成員是否初始化取決于編譯器,C++并沒有規定。對于沒有 顯?在初始化列表初始化的?定義類型成員會調?這個成員類型的默認構造函數,如果沒有默認構 造會編譯錯誤。

(6)初始化列表中按照成員變量在類中聲明順序進?初始化,跟成員在初始化列表出現的的先后順序? 關。建議聲明順序和初始化列表順序保持?致。

初始化列表:

?論是否顯?寫初始化列表,每個構造函數都有初始化列表;

?論是否在初始化列表顯?初始化成員變量,每個成員變量都要?初始化列表初始化;

練習題:

#include<iostream>
using namespace std;
class A
{public:A(int a):_a1(a), _a2(_a1)//初始化列表中按照成員變量在類中聲明順序進?初始化,跟成員在初始化列表出現的的先后順序?關{}void Print(){cout << _a1 << " " << _a2 << endl;}private:int _a2 = 2;//列表中已經進行了顯式的初始化,所以這里與缺省值無關了int _a1 = 2;
};int main()
{A aa(1);aa.Print();
}

先用a1初始化a2,但a1未進行初始化,所以a1是一個隨機值,a2就是一個隨機值。

再用a=1初始化a1,a1就是1

上述程序的運行結果是:輸出1和隨機值

四,類型轉換

(1)C++?持內置類型隱式類型轉換為類類型對象,需要有相關內置類型為參數的構造函數。

不同編譯器是否支持優化是不一樣的。

(2)構造函數前?加explicit就不再?持隱式類型轉換。

(3)類類型的對象之間也可以隱式轉換,需要相應的構造函數?持。

class A
{
public:// 構造函數explicit就不再支持隱式類型轉換// explicit A(int a1)A(int a1):_a1(a1){cout << "A(int a1)" << endl;}A(const A& aa){cout << "A(const A& aa)" << endl;}//explicit A(int a1, int a2)A(int a1, int a2):_a1(a1), _a2(a2){}void Print(){cout << _a1 << " " << _a2 << endl;}int Get() const{return _a1 + _a2;}
private:int _a1 = 1;int _a2 = 2;
};//自定義類型之間也可以進行類型轉換
class B
{
public:B(const A& a):_b(a.Get()){}
private:int _b = 0;
};class Stack
{
public:void Push(const A& aa)//臨時對象有常性{}void Push(const B& bb){}
};int main()
{// 構造A aa1(1);// 隱式類型轉換// 1構造臨時對象,臨時對象拷貝構造aa2,優化后直接構造// 1構造?個A的臨時對象,再?這個臨時對象拷?構造aa3 // 編譯器遇到連續構造+拷?構造->優化為直接構造 A aa2 = 1;A& aa3 = aa2;//前面加上一個const時為普通引用,不可行,因為臨時對象具有常性const A& aa4 = 1;Stack st;A aa10(10);st.Push(aa10);//原來需要定義一個aa10對象,拷貝構造給Push函數,再進行push操作//而類型轉換可以直接創建臨時變量進行拷貝構造,進行優化,傳參st.Push(10); // 隱式類型轉換支持這樣的間接傳參// C++11之后才?持多參數轉化 A aa3 = { 2,2 };A aa5(1, 1);// 隱式類型轉換// { 1, 1 }構造臨時對象,臨時對象拷貝構造aa5,優化后直接構造A aa6 = { 1, 1 };A aa11(11, 11);st.Push(aa11);st.Push({ 11, 11 }); // 隱式類型轉換支持這樣的間接傳參B b1(aa1);//自定義類型轉換為自定義類型// 隱式類型轉換B b1 = aa1;B b3(aa1);st.Push(b3);st.Push(aa1);return 0;
}

當優化關閉后,會出現構造+拷貝構造

五,static成員

static:

(1)?static修飾的成員變量,稱之為靜態成員變量,靜態成員變量?定要在類外進?初始化

(2)靜態成員變量為所有類對象所共享,不屬于某個具體的對象,不存在對象中,存放在靜態區,生命周期是全局的。因此對象大小不包括靜態成員

(3)?static修飾的成員函數,稱之為靜態成員函數,靜態成員函數沒有this指針

(4)靜態成員函數中可以訪問其他的靜態成員,但是不能訪問?靜態的,因為沒有this指針

(5)?靜態的成員函數,可以訪問任意的靜態成員變量和靜態成員函數

(6)突破類域就可以訪問靜態成員,可以通過類名::靜態成員或者對象.靜態成員來訪問靜態成員變量 和靜態成員函數

(7)靜態成員也是類的成員,受public、protected、private訪問限定符的限制

(8)靜態成員變量不能在聲明位置給缺省值初始化,因為缺省值是個構造函數初始化列表的,靜態成員 變量不屬于某個對象,不?構造函數初始化列表。

聲明:

private:// 類??聲明 static int _scount;

初始化:

// 類外?初始化 
int A::_scount = 0;

計算程序中創建了多少個類的對象

通過count看調用了多少次構造函數

靜態成員變量不走初始化列表,不能給缺省值

通過GetACount公有的成員函數對私有的靜態成員變量進行訪問。

靜態成員變量是公有時的訪問方式

1,靜態成員變量屬于所有對象,可以通過指定對象來訪問。

count<<a1._scount<<endl;

2,可以指定類域進行訪問。

count<<A::_scount<<endl;

注意:靜態成員函數無法對非靜態成員進行訪問

原因:(1)靜態成員沒有this指針

(2)而非靜態成員可以通過類域訪問

題目1:

思路:通過Solution成員函數構建對象,從而去調用Sum類域中的構造函數,構造對象大小有多大就調用多少次,并且是靜態成員變量,所以全局調用,每次使用的ret和i都是上次變化后的,來實現求和。

一般刷題網站都是支持變長數組的,但VS是不支持的

VS中更改:

Sum*ptr=new Sum[n];
delete[]ptr;//動態開辟
#include<iostream>
using namespace std;
class A
{
public:A(){++_scount;}
private://.......
};int main()
{A a1, a2;A a3(a1);//定義一個對象調用一次構造A aa1[10];//一次性定義多個對象,調用多次構造函數return 0;
}

題目2:

構造和析構順序

構造:全局變量在main函數之前創建,調用其構造函數,C最先調用,而main函數中靜態成員變量只要在使用時才會構造,所以先調用A,B的構造。同樣,對于多次調用有靜態變量函數只會調用一次構造函數,因為只會初始化一次。

析構:main函數棧幀先銷毀,先銷毀局部變量,后定義的先析構,對于全局變量(可以通過監視地址,進入析構函數監視this指針觀察)發現靜態成員變量先析構。

六,友元

1,流插入和提取的運算符重載

void TestDate6()
{Date d1(2025,3,12);printf("%");
}

對于printf和scanf只能打印內置類型,而對于自定義類型無法進行打印,C++為了解決這個問題,設計了流插入和流提取。

void TestDate6()
{int i = 22;double d = 1.2;cout << i;cout.operator<<(i);//運算符重載 相當于調用運算符重載調用函數,自動識別類型是因為重載機制,自動調用對應類型的重載cout << endl;cout << d;cout.operator<<(d);cout << i << " " << d << endl;*/
}

這里下圖更能直觀解釋每次重載會調用其對于類型的重載函數。

但是這種方法效率會降低,可以加上以下三行代碼提高效率。

ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

流插入ostream函數:

如:

ostream& operator<<(ostream& out){out << _year << "年" << _month << "月" << _day << "日" << endl;return out;}

但是對應的會出現錯誤

void TestDate6()
{//cout<<d1此時會報錯,因為左右參數配不上//d1 << cout;也會有一點小錯誤
}

參數配不上的圖解:這里out就是cout的別名

所以上述ostream函數有錯誤,不能重載為成員函數,否則參數會對不上。

//cout<<d1參數對應上了
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}

這里問題就是訪問私有變量:

解決方法:(1)提供一個get成員函數? (2)聲明為友元函數

......
//友元聲明  可以放到任意位置,一般放到類開始
friend ostream& operator<<(ostream& out, const Date& d);
private:int _year;int _month;int _day;
};

但是此時友元為全局函數,會在多個文件中包含,符號表就會重復,需要聲明和定義分離。

解決方法:(1)h中聲明,cpp中定義? (2)在函數前加上inline

流提取函數:

istream& operator>>(istream& in, Date& d);

不加const,因為提取的值要放到該對象中,所以加引用。

友元聲明:

friend istream& operator>>(istream& in, Date& d);
inline istream& operator>>(istream& in, Date& d)
{while (1){cout << "請依次輸入年月日:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "輸入日期非法,請重新輸入!!!" << endl;}else{break;}}return in;
}void TestDate6()
{Date d1(2025, 3, 0);Date d2;cin >> d1 >> d2;//連續輸入輸出cout << d1 << d2;
}

2友元

(1)友元提供了?種突破類訪問限定符封裝的?式,友元分為:友元函數和友元類,在函數聲明或者類 聲明的前?加friend,并且把友元聲明放到?個類的??

(2)外部友元函數可訪問類的私有和保護成員,友元函數僅僅是?種聲明,他不是類的成員函數

(3)友元函數可以在類定義的任何地?聲明,不受類訪問限定符限制

(4)?個函數可以是多個類的友元函數

void func(const A& aa, const B& bb)
{cout << aa._a1 << endl;cout << bb._b1 << endl;
}
//可以把這個函數既放到類域A中又放到類域B中

(5)友元類中的成員函數都可以是另?個類的友元函數,都可以訪問另?個類中的私有和保護成員。

// 前置聲明,否則A的友元函數聲明編譯器不認識B 
class B;
class A
{// 友元聲明 friend void func(const A& aa, const B& bb);
private:int _a1 = 1;int _a2 = 2;
};
class A
{// 友元聲明 friend class B;
private:int _a1 = 1;int _a2 = 2;
};
class B
{
public:void func1(const A& aa){cout << aa._a1 << endl;cout << _b1 << endl;}void func2(const A& aa){cout << aa._a2 << endl;cout << _b2 << endl;}
private:int _b1 = 3;int _b2 = 4;
};

(6)友元類的關系是單向的,不具有交換性,?如A類是B類的友元,但是B類不是A類的友元。

(7)友元類關系不能傳遞,如果A是B的友元,B是C的友元,但是A不是C的友元。

(8)有時提供了便利。但是友元會增加耦合度,破壞了封裝,所以友元不宜多?。

七,內部類

(1)如果?個類定義在另?個類的內部,這個內部類就叫做內部類。內部類是?個獨?的類,跟定義在 全局相?,他只是受外部類類域限制和訪問限定符限制,所以外部類定義的對象中不包含內部類。

(2)內部類默認是外部類的友元類。

(3)內部類本質也是?種封裝,當A類跟B類緊密關聯,A類實現出來主要就是給B類使?,那么可以考 慮把A類設計為B的內部類,如果放到private/protected位置,那么A類就是B類的專屬內部類,其 他地?都?不了。

class A
{
private:static int _k;int _h = 1;
public:class B // B默認就是A的友元 {public:void foo(const A& a){cout << _k << endl; //OKcout << a._h << endl; //OK}int _b1;};
};int A::_k = 1;int main()
{cout << sizeof(A) << endl;//B:: _b1; 是訪問不到的,受到A類域的限制A::B b;A aa;b.foo(aa);return 0;
}

題目1:

求1+2+3+...+n_牛客題霸_牛客網

class Solution 
{// 內部類 class Sum{public:Sum(){_ret += _i;++_i;}};static int _i;static int _ret;public:int Sum_Solution(int n){// 變?數組 Sum arr[n];return _ret;}
};
int Solution::_i = 1;
int Solution::_ret = 0;

圖解:

八,匿名對象

臨時對象:(1)類型轉換時會產生? ?(2)傳值返回會產生

1,?類型(實參)定義出來的對象叫做匿名對象,相?之前我們定義的類型對象名(實參)定義出來的 叫有名對象。

2,匿名對象?命周期只在當前??,?般臨時定義?個對象當前??下即可,就可以定義匿名對象。

int main()
{// 有名對象A aa1(1);// 轉換產生的臨時對象  const引用可以延長其生命周期const A& aa2 = 1;// 但是他的?命周期只有這??,我們可以看到下??他就會?動調?析構函數 // 匿名對象A(2);A aa1;// 不能這么定義對象,因為編譯器?法識別下?是?個函數聲明,還是對象定義 //A aa1();// 但是我們可以這么定義匿名對象,匿名對象的特點不?取名字, return 0;
}
class Solution 
{public:int Sum_Solution(int n){//...return n;}
};
int main()
{//有名調用Solution s;s.Sum_Solution(10);//匿名調用Solution().Sum_Solution(10);
}

九,對象拷貝時的編譯器優化

(1)現代編譯器會為了盡可能提?程序的效率,在不影響正確性的情況下會盡可能減少?些傳參和傳返 回值的過程中可以省略的拷?。

(2)如何優化C++標準并沒有嚴格規定,各個編譯器會根據情況??處理。當前主流的相對新?點的編 譯器對于連續?個表達式步驟中的連續拷?會進?合并優化,有些更新更"激進"的編譯器還會進? 跨?跨表達式的合并優化。

(3)linux下可以將下?代碼拷?到test.cpp?件,編譯時? g++ test.cpp -fno-elide-constructors 的?式關閉構造相關的優化。

int main()
{// 構造臨時對象,臨時對象再拷貝構造aa1,優化為直接構造//A aa1 = 1;// 傳值傳參// 無優化A aa1;//f1(aa1); 已經創建出對象,再去拷貝構造是沒有優化的//cout << endl;//下面兩者情況是有優化的隱式類型,連續構造+拷貝構造->優化為直接構造f1(1);一個表達式中,連續構造+拷貝構造->優化為一個構造f1(A(2));//cout << endl;
}

圖解觀察:

傳值返回:先構造,再創建臨時對象,返回臨時對象,再調用拷貝構造。

A f2()
{A aa;return aa;
}int main()
{// 傳值返回// 返回時一個表達式中,連續拷貝構造+拷貝構造->優化一個拷貝構造 (vs2019 debug)// 一些編譯器會優化得更厲害,進行跨行合并優化,直接變為構造。(vs2022 debug)f2();cout << endl;// 返回時一個表達式中,連續拷貝構造+拷貝構造->優化一個拷貝構造 (vs2019 debug)// 一些編譯器會優化得更厲害,進行跨行合并優化,直接變為構造。(vs2022 debug)A aa2 = f2();cout << endl;// 接收返回值時A ret=f2();cout<<endl;
}

未接收返回值時圖解:左側關閉優化,右側開啟

接收返回值時圖解:構造+拷貝構造+析構,返回值給ret時又要拷貝構造+析構。

拷貝構造+賦值重載:

未優化:構造+拷貝構造+析構+賦值重載+析構

int main()
{// 一個表達式中,連續拷貝構造+賦值重載->無法優化aa1 = f2();cout << endl;return 0;
}

圖解:

總結:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/905646.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/905646.shtml
英文地址,請注明出處:http://en.pswp.cn/news/905646.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大數據架構選型分析

選擇依據 1.業務需求與技術要求 用戶需要根據自己的業務需求來選擇架構&#xff0c;如果業務對于Hadoop、Spark、Strom等關鍵技術有強制性依賴&#xff0c;選擇Lambda架構可能較為合適&#xff1b;如果處理數據偏好于流式計算&#xff0c;又依賴Flink計算引擎&#xff0c;那么…

Trae 插件 Builder 模式:從 0 到 1 開發天氣查詢小程序,解鎖 AI 編程新體驗

在軟件開發領域&#xff0c;效率與創新始終是開發者追求的核心目標。Trae 插件&#xff08;原 MarsCode 編程助手&#xff09;Builder 模式的全面上線&#xff0c;無疑為開發者帶來了全新的解決方案。它不僅同時支持 VS Code、JetBrains IDEs 等主流開發環境&#xff0c;還能讓…

SSM項目集成redis、Linux服務器安裝redis

在SSM&#xff08;Spring Spring MVC MyBatis&#xff09;項目中引入Redis主要分為以下步驟&#xff0c;確保配置正確并能在業務中靈活使用&#xff1a; 1. 添加Redis依賴?? 在Maven的pom.xml中添加Spring Data Redis和Jedis&#xff08;或Lettuce&#xff09;依賴&#…

【Redis】壓縮列表

目錄 1、背景2、壓縮列表【1】底層結構【2】特性【3】優缺點 1、背景 ziplist&#xff08;壓縮列表&#xff09;是redis中一種特殊編碼的雙向鏈表數據結構&#xff0c;主要用于存儲小型列表和哈希表。它通過緊湊的內存布局和特殊的編碼方式來節省內存空間。 2、壓縮列表 【1…

LocalDateTime類型的時間在前端頁面不顯示或者修改數據時因為LocalDateTime導致無法修改,解決方案

1.數據庫中的時間數據&#xff0c;在控制臺可以正常返回&#xff0c;在前端無法返回&#xff0c;即顯示空白&#xff0c;如下圖所示: 2.這種問題一般時由于數據庫和我們實體類的名稱不一致引起的&#xff0c;我們數據庫一般采用_的方式命名&#xff0c;但是在Java中我們一般采用…

Spring框架核心技術深度解析:JDBC模板、模擬轉賬與事務管理

一、JDBC模板技術&#xff1a;簡化數據庫操作 在傳統JDBC開發中&#xff0c;繁瑣的資源管理和重復代碼一直是開發者的痛點。Spring框架提供的 JDBC模板&#xff08;JdbcTemplate) 徹底改變了這一現狀&#xff0c;它通過封裝底層JDBC操作&#xff0c;讓開發者僅需關注SQL邏輯&a…

Modern C++(一)基本概念

1、基本概念 1.1、注釋 注釋在翻譯階段3會被替換為單個空白字符從程序中移除 1.2、名字與標識符 標識符是一個由數字、下劃線、大小寫字符組成的任意長度序列。有效的標識符首個字符必須是以A-Z、a-z、下劃線開頭&#xff0c;。有效的標識符其他字符可以是0-9、A-Z、a-z、下…

STM32的TIMx中Prescaler和ClockDivision的區別

Prescaler預分頻&#xff0c;以筆者目前的學習程度來說&#xff0c;這個參數&#xff0c;一般來說是對主時鐘進行分頻后的計數器時鐘。這個預分頻后的時鐘主要是用于的計數的。 這個主時鐘&#xff0c;對于時基單元來說可以是內部時鐘&#xff0c;也可以是外部時鐘。一般來說我…

前端性能指標及優化策略——從加載、渲染和交互階段分別解讀詳解并以Webpack+Vue項目為例進行解讀

按照加載階段、渲染階段和交互階段三個維度進行系統性闡述&#xff1a; 在現代 Web 開發中&#xff0c;性能不再是錦上添花&#xff0c;而是決定用戶體驗與業務成敗的關鍵因素。為了全面監控與優化網頁性能&#xff0c;我們可以將性能指標劃分為加載階段、渲染階段、和交互階段…

MySQL——1、數據庫基礎

數據庫基礎 1、安裝MySQL2、什么是數據庫3、數據庫使用案例4、MySQL架構與SQL分類5、存儲引擎 1、安裝MySQL 1、更新軟件包列表 sudo apt update2、查看MySQL安裝包 apt list | grep mysql-server3、安裝MySQL # 默認安裝最新版 sudo apt install -y mysql-server4、啟動My…

ET MailBoxComponent類(實體) 分析

MailBoxComponent 作用是&#xff0c;用來接收Actor消息&#xff0c;處理Actor消息。這個沒有存儲能&#xff0c;收到消息后立即就處理了。ParentInstanceId 是MailBox所在的實體InstanceIdMailBoxType MailBox類型MailBoxInvoker 分發消息的包裝Add 方法&#xff0c;看名字是…

Weblogic SSRF漏洞復現(CVE-2014-4210)【vulhub靶場】

漏洞概述&#xff1a; Weblogic中存在一個SSRF漏洞&#xff0c;利用該漏洞可以發送任意HTTP請求&#xff0c;進而攻擊內網中redis、fastcgi等脆弱組件。 漏洞形成原因&#xff1a; WebLogic Server 的 UDDI 組件&#xff08;uddiexplorer.war&#xff09;中的 SearchPublicR…

js應用opencv

思路&#xff1a; 第一步&#xff1a;直方圖 第二步&#xff1a;獲得直方圖的波峰 第三步&#xff1a;波峰勝負10&#xff0c;高于或低于變紅色 1.引用import cv from ‘techstark/opencv-js’; 2.vue代碼 <div class"historyLeft2"><div style"relat…

用Python代碼繪制動態3D愛心效果

引言 介紹Python在創意編程中的應用&#xff0c;特別是如何通過簡單的代碼實現視覺上的美感。引出本文將分享的愛心代碼&#xff0c;并簡要說明其實現原理。 愛心代碼的基本實現 展示一個簡單的Python代碼示例&#xff0c;使用字符畫的方式在控制臺中繪制一個愛心圖案。 pr…

使用Python開發經典俄羅斯方塊游戲

使用Python開發經典俄羅斯方塊游戲 在這篇教程中&#xff0c;我們將學習如何使用Python和Pygame庫開發一個經典的俄羅斯方塊游戲。這個項目將幫助你理解游戲開發的基本概念&#xff0c;包括圖形界面、用戶輸入處理、碰撞檢測等重要內容。 項目概述 我們將實現以下功能&…

兼顧長、短視頻任務的無人機具身理解!AirVista-II:面向動態場景語義理解的無人機具身智能體系統

作者&#xff1a;Fei Lin 1 ^{1} 1, Yonglin Tian 2 ^{2} 2, Tengchao Zhang 1 ^{1} 1, Jun Huang 1 ^{1} 1, Sangtian Guan 1 ^{1} 1, and Fei-Yue Wang 2 , 1 ^{2,1} 2,1單位&#xff1a; 1 ^{1} 1澳門科技大學創新工程學院工程科學系&#xff0c; 2 ^{2} 2中科院自動化研究所…

【藍橋杯省賽真題49】python偶數 第十五屆藍橋杯青少組Python編程省賽真題解析

python偶數 第十五屆藍橋杯青少組python比賽省賽真題詳細解析 博主推薦 所有考級比賽學習相關資料合集【推薦收藏】1、Python比賽 信息素養大賽Python編程挑戰賽 藍橋杯python選拔賽真題詳解

鴻蒙(HarmonyOS)應用開發入門教程

目錄 第一章:鴻蒙系統簡介 1.1 什么是鴻蒙系統? 1.2 鴻蒙系統架構 第二章:開發環境搭建 2.1 安裝DevEco Studio 步驟1:下載與安裝 步驟2:首次配置 步驟3:設備準備 2.2 創建第一個項目 第三章:鴻蒙應用開發基礎 3.1 核心概念:Ability與AbilitySlice 示例代碼…

VM中 ubuntu 網卡不顯示

1.添加網卡配置 #sudo nano /etc/netplan/01-netcfg.yaml network:version: 2renderer: networkdethernets:ens33:dhcp4: trueens37:dhcp4: trueens38:dhcp4: true#保存后 sudo netplan apply2.查看網絡狀態 sudo systemctl start systemd-networkd sudo systemctl status sy…

阿克曼-幻宇機器人系列教程3- 機器人交互實踐(Message)

上一篇文章介紹了如何通過topic操作命令實現與機器人的交互&#xff0c;本篇我們介紹如何通過Message&#xff08;即topic的下一級&#xff09;實現與機器人的交互。 和topic一樣&#xff0c;首先在一個終端通過ssh命令登錄機器人、啟動機器人&#xff0c;然后打開另外一個終端…