const&&內聯函數&&友元函數&&static&&N中拷貝構造的優化
1. const
關鍵字const
- const使用規則:
- const修飾變數,變數具有常屬性,不可改
- const修飾指針:
- ① const int* p; 指針指向內容不可修改
- ② int* const p; 指針不可修改
- ③ const int* const p; 指針和指向的內容都不可修改
- const修飾函數
- ①常引用:修飾參數,防止參數被修改;例:bool operator==(const Date& d);
- ② 常函數:修飾類的成員函數,表示此函數不允許改變修改函數的數據成員
注意:const修飾的函數或變數,許可權只可縮小不可放大
例:
void Func1() const //常函數
{
_a=10; //錯誤,數據不可修改
Func2(); //const不能調用非const,許可權放大
}
void Func2()
{
Func1();//非const可調用const,許可權縮小
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
const修飾成員函數
在成員函數後面加const,本質上const修飾的是this指針所指向的對象,也就保證調用這個const成員函數時,成員對象在函數內不會被改變。
2. 內聯函數
定義
- 以inline修飾的函數叫做內聯函數,編譯時C++編譯器會調用內聯函數的地方展開,沒有函數壓棧的開銷,內聯函數提升程序運行的效率。
特點
- ① 類的成員函數默認為內聯函數
- ② 以空間換取時間,省去函數棧幀的開銷
- ③ inline必須函數定義放在一起,才能成為內聯函數,僅將inline放在聲明前是不起不作用的。
- ④ 內部代碼較短,編譯器會自動優化 當函數體內代碼很長或有遞歸時,內聯不會展開
優缺點
優點:
當函數體比較小的時候, 內聯該函數可以令目標代碼更加高效. 對於存取函數以及其它函數體比較短, 性能關鍵的函數, 鼓勵使用內聯.缺點:
濫用內聯將導致程序變慢. 內聯可能使目標代碼量或增或減, 這取決於內聯函數的大小. 內聯非常短小的存取函數通常會減少代碼大小, 但內聯一個相當大的函數將戲劇性的增加代碼大小. 現代處理器由於更好的利用了指令緩存, 小巧的代碼往往執行更快。使用原則
- ① 一個較為合理的經驗準則是, 不要內聯超過 10 行的函數.
- ② 內聯那些包含循環或 switch 語句的函數常常是得不償失 (除非在大多數情況下, 這些循環或 switch 語句從不被執行).
- ③ 有些函數即使聲明為內聯的也不一定會被編譯器內聯,比如遞歸函數
3. 內聯函數與宏
原因:
- 考慮到宏的一些缺點,在C++中,建議使用 內聯替代宏;內聯函數替代宏即能達到宏的效率,在某些問題上也能自由訪問類的數據成員;
宏的優缺點:
優點:
增強代碼的復用性,增強性能缺點:
不方便調試;可讀性差,容易誤用,可維護性差;無類型安全檢查
在書《高質量程序設計指南——C++/C語言》中這樣解釋到:
4. 友元函數
- 定義
- 在C++中友元函數允許在類外訪問該類中的任何成員,就象成員函數一樣,友元函數用關鍵字friend說明。
- 特點
- ① 友元函數不是類的成員函數,必須採用全局
- ② 友元函數在一定程度上破壞了類的封裝性,可以從類外面訪問私有成員
- ③ 整個類可以是另一個類的友元。友元類的每個成員函數都是另一個類的友元函數,都可訪問另一個類中的保護或私有數據成員。
- 例子
class AA
{
friend void Show(const AA& A);//聲明
public:
private:
int _a;
int _b;
}
//全局(特點 ①)
void Show()
{
cout<<A._a<<end;//訪問了私有成員,特點②
cout<<A._b<<endl;
}
//友元類(特點 ③)
class AA
{
// BB是AA 的友元,所以 BB可以訪問AA的所有成員。
friend class BB;
private :
int _a1;
int _a2 ;
};
class BB
{
public :
void Display ()
{
cout<<_b1<< endl;
cout<<_b2<< endl;
// 定義為友元類後,可以訪問AA類對象的所有成員
cout<<_A. _a1<<endl ;
cout<<_A. _a2<<endl ;
}
private :
int _b1 ;
int _b2 ;
AA _A;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
應用場景—輸入輸出運算符的重載
- 輸入輸出指的是從標準輸入或者從標準輸出,可以將其看作是數據的流向:數據流向某一區域,而這一區域所指的就是標準輸入(硬碟,內存等)或標準輸出(顯示器,文件等)
class Date
{
public :
friend ostream & operator<< ( ostream& os , const Date& d );
friend istream & operator>> ( istream& is , Date& d);
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
//輸出運算符的重載
ostream & operator<<( ostream& out , const Date& d)
{
//對象d流向ostream& out,返回out,enl流向out
out<<"year:" <<d. _year<<endl ;
out<<"month:" <<d. _month<<endl ;
out<<"day:" <<d. _day<<endl <<endl;
return out ;
}
//輸出運算符的重載
istream & operator>> ( istream& in , Date& d)
{
cout<<" 請分別輸入年月日: "<<endl ;
in>>d ._year;
in>>d ._month;
in>>d ._day;
return in ;
}
void Test ()
{
Date d1 ;
cin>>d1 ;
cout<<d1 ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
5. 類的靜態成員
- 成員變數
- ① 類裡面static修飾的變數稱為靜態類成員;
- ② 靜態類成員屬於類當中的所有對象
class Date
{
public:
Date()
{
cout<<"Date()"<<endl;
++count;
}
private:
int _year;
int _month;
int _day;
static size_t count;//靜態成員變數,統計日期創建的個數
}
size_t Date::count=0;//初始化時注意指明類域
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 成員函數
① 調用靜態成員函數時,使用類型::作用域訪問符直接調用靜態成員函數
② 靜態成員函數沒有隱含的this指針,只能訪問靜態成員變數
③ 靜態成員函數不能訪問非靜態成員變數,不允許許可權放大
④ 非靜態成員函數可以訪問靜態成員變數,許可權縮小
class Date
{
public :
Date ()
{
cout<<"Date ()" <<endl;
++ count;
}
void Display ()
{
cout<<"year:" <<_year<< endl;
cout<<"month:" <<_month<< endl;
cout<<"day:" <<_day<< endl;
}
// 靜態成員函數
static void PrintCount()
{
cout<<"Date count:" <<count<< endl;//只能訪問靜態成員變數
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
static size_t count; // 靜態成員變數,統計創建時間個數
};
// 定義並初始化靜態成員變數
int Date::sCount = 0;
void Test ()
{
Date d1 ,d2;
// 訪問靜態成員
Date::PrintCount ();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
6. 拷貝構造函數的優化
函數中調用中拷貝構造原則:
- 對象作返回值傳值時,生成匿名對象,此匿名對象調用拷貝構造函數
- 對象作參數傳值時,傳參過程中形成匿名對象,此匿名對象調用拷貝構造函數
優化原則
- 表達式為一條語句時,構造和拷貝構造進行合併
- 拷貝構造出來的匿名對象返回馬上作為下一個拷貝構造函數的參數時,兩次拷貝構造函數合併成一個
- 當對象已經創建出來,需要構造新的對象或匿名對象時,不會發生優化
例:
class Date
{
public:
Date()
{
cout << "Date()" << endl;
}
Date(const Date& d)
{
cout << "Date(const Date& d)" << endl;
}
Date& operator =(const Date& d)
{
cout << "Date& operator=(const Date& d)" << endl;
return *this;
}
~Date()
{
cout << "~Date()" << endl;
}
};
// 1.Date 對象做參數傳值 & 傳引用
void fun1(Date d) //void fun1(Date& d)
{}
// 2.Date 對象做返回值傳值 & 傳引用
Date& fun2() // Date& fun2()
{
Date d;
return d;
}
// 3.Date 對象做臨時返回值傳值 &傳引用(編譯器優化問題)
Date fun3() // Date& fun3()
{
return Date();
}
int main()
{
// 場景
Date d1;
fun1(d1);
// 場景2
Date d2 = fun2();
// 場景3
Date d3;
d3 = fun3();
system("pause");
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
分析:
場景1
參數為對象:一次構造,一次拷貝構造,兩次析構(拷貝構造原則2,優化原則2)
參數為引用:一次構造
場景2
返回值為對象:一次構造,一次拷貝構造,兩次析構(拷貝構造原則1,優化原則2)
返回值為引用:一次構造,一次拷貝構造,一次析構
場景3
對象作返回值傳值: 兩次構造,一次賦值運算符重載(拷貝構造原則1,優化原則1)
對象作返回值傳引用:兩次構造,一次賦值運算符重載
7. 幾個習題
Test1中調用了 2 次AA的拷貝構造函數, 1 次AA的賦值運算符函數的重載。
Test2中調用了2 次AA的拷貝構造函數, 0 次AA的賦值運算符函數的重載。
Test3中調用了3 次AA的拷貝構造函數, 0 次AA的賦值運算符函數的重載。
class AA
{};
AA f (AA a)
{
return a ;
}
void Test1 ()
{
AA a1 ;
a1 = f(a1);
}
void Test2 ()
{
AA a1 ;
AA a2 = f(a1);
}
void Test3 ()
{
AA a1 ;
AA a2 = f(f(a1));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
分析:
Test1中調用了 2 次AA的拷貝構造函數, 1 次AA的賦值運算符函數的重載。
Test2中調用了2 次AA的拷貝構造函數, 0 次AA的賦值運算符函數的重載。
Test3中調用了3 次AA的拷貝構造函數, 0 次AA的賦值運算符函數的重載。
※15 種排序演算法可視化展示
※ES6——Promise使用經驗
TAG:程序員小新人學習 |