1. 再探構造函數
? 之前我們實現構造函數時,初始化成員變量主要使?函數體內賦值,構造函數初始化還有?種?
式,就是初始化列表,初始化列表的使??式是以?個冒號開始,接著是?個以逗號分隔的數據成
員列表,每個"成員變量"后?跟?個放在括號中的初始值或表達式。
? 每個成員變量在初始化列表中只能出現?次,語法理解上初始化列表可以認為是每個成員變量定義初始化的地?。
? 引?成員變量,const成員變量,沒有默認構造的類類型變量,必須放在初始化列表位置進?初始
化,否則會編譯報錯。
? C++11?持在成員變量聲明的位置給缺省值,這個缺省值主要是給沒有顯?在初始化列表初始化的成員使?的。
? 盡量使?初始化列表初始化,因為那些你不在初始化列表初始化的成員也會?初始化列表,如果這個成員在聲明位置給了缺省值,初始化列表會?這個缺省值初始化。如果你沒有給缺省值,對于沒有顯?在初始化列表初始化的內置類型成員是否初始化取決于編譯器,C++并沒有規定。對于沒有顯?在初始化列表初始化的?定義類型成員會調?這個成員類型的默認構造函數,如果沒有默認構造會編譯錯誤。
? 初始化列表中按照成員變量在類中聲明順序進?初始化,跟成員在初始化列表出現的的先后順序?關。建議聲明順序和初始化列表順序保持?致。
初始化列表總結:
?論是否顯?寫初始化列表,每個構造函數都有初始化列表;
?論是否在初始化列表顯?初始化,每個成員變量都要?初始化列表初始化;
#include<iostream>
using namespace std;class Time
{
public:Time(int hour): _hour(hour){cout << "Time()" << endl;}
private:int _hour;};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
};int main()
{int i = 0;Date d1(i);d1.Print();return 0;
}
#include<iostream>
using namespace std;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;int _month = 1;int _day;Time _t = 1;const int _n = 1;int* _ptr = (int*)malloc(12);};int main()
{Date d1;d1.Print();return 0;
}
2. 類型轉換
? C++?持內置類型隱式類型轉換為類類型對象,需要有相關內置類型為參數的構造函數。
? 構造函數前?加explicit就不再?持隱式類型轉換。
? 類類型的對象之間也可以隱式轉換,需要相應的構造函數?持。
#include<iostream>
using namespace std;
class A
{
public:// 構造函數explicit就不再?持隱式類型轉換// explicit A(int a1)A(int a1):_a1(a1){}//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;
};
int main()
{// 1構造?個A的臨時對象,再?這個臨時對象拷?構造aa3// 編譯器遇到連續構造+拷?構造->優化為直接構造A aa1 = 1;aa1.Print();const A& aa2 = 1;// C++11之后才?持多參數轉化A aa3 = { 2,2 };// aa3隱式類型轉換為b對象// 原理跟上?類似B b = aa3;const B& rb = aa3;return 0;
}
3. static成員
? ?static修飾的成員變量,稱之為靜態成員變量,靜態成員變量?定要在類外進?初始化。
? 靜態成員變量為所有類對象所共享,不屬于某個具體的對象,不存在對象中,存放在靜態區。
? ?static修飾的成員函數,稱之為靜態成員函數,靜態成員函數沒有this指針。
? 靜態成員函數中可以訪問其他的靜態成員,但是不能訪問?靜態的,因為沒有this指針。
? ?靜態的成員函數,可以訪問任意的靜態成員變量和靜態成員函數。
? 突破類域就可以訪問靜態成員,可以通過類名::靜態成員 或者 對象.靜態成員 來訪問靜態成員變量和靜態成員函數。
? 靜態成員也是類的成員,受public、protected、private 訪問限定符的限制。
? 靜態成員變量不能在聲明位置給缺省值初始化,因為缺省值是個構造函數初始化列表的,靜態成員變量不屬于某個對象,不?構造函數初始化列表。
#include<iostream>
using namespace std;
class A
{
public:A(){++_scount;}A(const A& t){++_scount;}~A(){--_scount;}static int GetACount(){return _scount;}
private:// 類??聲明static int _scount;
};
// 類外?初始化
int A::_scount = 0;
int main()
{cout << A::GetACount() << endl;A a1, a2;A a3(a1);cout << A::GetACount() << endl;cout << a1.GetACount() << endl;// 編譯報錯:error C2248: “A::_scount”: ?法訪問 private 成員(在“A”類中聲明)//cout << A::_scount << endl;return 0;
}
4. 友元
? 友元提供了?種突破類訪問限定符封裝的?式,友元分為:友元函數和友元類,在函數聲明或者類聲明的前?加friend,并且把友元聲明放到?個類的??。
? 外部友元函數可訪問類的私有和保護成員,友元函數僅僅是?種聲明,他不是類的成員函數。
? 友元函數可以在類定義的任何地?聲明,不受類訪問限定符限制。
? ?個函數可以是多個類的友元函數。
? 友元類中的成員函數都可以是另?個類的友元函數,都可以訪問另?個類中的私有和保護成員。
? 友元類的關系是單向的,不具有交換性,?如A類是B類的友元,但是B類不是A類的友元。
? 友元類關系不能傳遞,如果A是B的友元, B是C的友元,但是A不是C的友元。
? 有時提供了便利。但是友元會增加耦合度,破壞了封裝,所以友元不宜多?。
#include<iostream>
using namespace std;
// 前置聲明,都則A的友元函數聲明編譯器不認識B
class B;
class A
{// 友元聲明friend void func(const A & aa, const B & bb);
private:int _a1 = 1;int _a2 = 2;};class B
{// 友元聲明friend void func(const A & aa, const B & bb);
private:int _b1 = 3;int _b2 = 4;
};void func(const A & aa, const B & bb)
{cout << aa._a1 << endl;cout << bb._b1 << endl;
}int main()
{A aa;
B bb;
func(aa, bb);return 0;
}
#include<iostream>
using namespace std;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;
};
int main()
{A aa;B bb;bb.func1(aa);bb.func1(aa);return 0;
}
5. 內部類
? 如果?個類定義在另?個類的內部,這個內部類就叫做內部類。內部類是?個獨?的類,跟定義在全局相?,他只是受外部類類域限制和訪問限定符限制,所以外部類定義的對象中不包含內部類。
? 內部類默認是外部類的友元類。
? 內部類本質也是?種封裝,當A類跟B類緊密關聯,A類實現出來主要就是給B類使?,那么可以考慮把A類設計為B的內部類,如果放到private/protected位置,那么A類就是B類的專屬內部類,其
他地?都?不了。
#include<iostream>
using namespace std;
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 A::_k = 1;int main()
{cout << sizeof(A) << endl;A::B b;A aa;b.foo(aa);return 0;
}
6. 匿名對象
? ? 類型(實參) 定義出來的對象叫做匿名對象,相?之前我們定義的 類型 對象名(實參) 定義出來的叫有名對象
? 匿名對象?命周期只在當前??,?般臨時定義?個對象當前??下即可,就可以定義匿名對象。
#include <iostream>
using namespace std;
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};
int main()
{A aa1;// 不能這么定義對象,因為編譯器?法識別下?是?個函數聲明,還是對象定義//A aa1();// 但是我們可以這么定義匿名對象,匿名對象的特點不?取名字,// 但是他的?命周期只有這??,我們可以看到下??他就會?動調?析構函數A();A(1);A aa2(2);// 匿名對象在這樣場景下就很好?,當然還有?些其他使?場景,這個我們以后遇到了再說Solution().Sum_Solution(10);return 0;
}
7. 對象拷?時的編譯器優化
? 現代編譯器會為了盡可能提?程序的效率,在不影響正確性的情況下會盡可能減少?些傳參和傳返回值的過程中可以省略的拷?。
? 如何優化C++標準并沒有嚴格規定,各個編譯器會根據情況??處理。當前主流的相對新?點的編譯器對于連續?個表達式步驟中的連續拷?會進?合并優化,有些更新更"激進"的編譯器還會進?跨?跨表達式的合并優化。
#include<iostream>
using namespace std;
class A
{
public:A(int a = 0):_a1(a){cout << "A(int a)" << endl;}A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a1 = aa._a1;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a1 = 1;};
void f1(A aa)
{}A f2()
{A aa;return aa;}int main()
{// 傳值傳參A aa1;f1(aa1);cout << endl;// 隱式類型,連續構造+拷?構造->優化為直接構造f1(1);// ?個表達式中,連續構造+拷?構造->優化為?個構造f1(A(2));cout << endl;cout << "***********************************************" << endl;// 傳值返回// 返回時?個表達式中,連續拷?構造+拷?構造->優化?個拷?構造 (vs2019 debug)// ?些編譯器會優化得更厲害,進?跨?合并優化,直接變為構造。(vs2022 debug)f2();cout << endl;// 返回時?個表達式中,連續拷?構造+拷?構造->優化?個拷?構造 (vs2019 debug)// ?些編譯器會優化得更厲害,進?跨?合并優化,直接變為構造。(vs2022 debug)A aa2 = f2();cout << endl;// ?個表達式中,連續拷?構造+賦值重載->?法優化aa1 = f2();cout << endl;return 0;}
總結:
演講主要是對內核對象的進一步深入了解。再探構造函數,我們增加了初始化列表。類型轉換中,內置類型可直接由影視類型轉換。而自定義類型要重寫重載函數。再來就是對static成員、友元和內部類還有匿名對象的補充。如果覺得博主講的不錯的話,請留下點贊和收藏吧,謝謝!未完待續...