目錄
- static成員
- 概念
- 靜態成員變量
- 面試題
- 補充
- 代碼1
- 代碼2
- 代碼3
- 如何訪問private中的成員變量
- 靜態成員函數
- 靜態成員函數沒有this指針
- 特性
- 友元
- 友元函數
- 友元類
- 內部類
- 特性1
- 特性2
- 匿名對象
- 拷貝對象時的一些編譯器優化
感謝各位大佬對我的支持,如果我的文章對你有用,歡迎點擊以下鏈接
🐒🐒🐒 個人主頁
🥸🥸🥸 C語言
🐿?🐿?🐿? C語言例題
🐣🐣🐣 python
🐓🐓🐓 數據結構C語言
🐔🐔🐔 C++
🐿?🐿?🐿? 文章鏈接目錄
static成員
概念
聲明為static的類成員稱為類的靜態成員,用static修飾的成員變量,稱之為靜態成員變量
用static修飾的成員函數,稱之為靜態成員函數。
靜態成員變量一定要在類外進行初始化
靜態成員變量
面試題
面試題:實現一個類,計算程序中創建出了多少個類對象。
class A
{
public:A(){++n;}A(const A& aa){++ n;}
private:static int n;
};
int A::n = 0;
這里的n就是一個靜態全局變量,注意靜態變量是不能給缺省值的,因為他不是單獨屬于某一個對象,而是屬于這個類的所有對象,因此需要在類外面定義
由于n受域作用限定符的限制,當我們屏蔽掉private后就可以訪問n了
我們再來看看下面的三段代碼
補充
代碼1
class A
{
public:A(){++n;}A(const A& aa){++ n;}
//private:static int n;
};
int A::n = 0;
int main()
{A aa1;A aa2;A* ptr = nullptr;cout << aa1.n << endl;cout << aa2.n << endl;cout << ptr->n << endl;return
代碼2
class A
{
public:A(){++n;}A(const A& aa){++ n;}
//private:static int n;
};
int A::n = 0;
int main()
{A aa1;//A aa2;A* ptr = nullptr;cout << aa1.n << endl;//cout << aa2.n << endl;cout << ptr->n << endl;return 0;
}
代碼3
class A
{
public:A(){++n;}A(const A& aa){++ n;}
//private:static int n;
};
int A::n = 0;
int main()
{//A aa1;//A aa2;A* ptr = nullptr;//cout << aa1.n << endl;//cout << aa2.n << endl;cout << ptr->n << endl;return 0;
}
上面的三個代碼中ptr輸出的n的值是不一樣的,這需要我們了解static存儲的變量在靜態區
比如ptr->n,n并不在ptr指向的對象里,而是在靜態區,在尋找n的時候就是去靜態區里找
因為是受到static修飾,所以n的值是全局變量,全局變量不想局部變量,出了作用域后就會銷毀然后從新開始,也就是說這里的n不會因為一個對象結束后就重新變成0
如何訪問private中的成員變量
上面的代碼中我們都是將private屏蔽掉才可以訪問到n的,當private沒有屏蔽的時候,就會因為權限導出不允許訪問
要想解決這個問題只有在公有區域里創建一個函數Getn()去獲得n的值
class A
{
public:A(){++n;}A(const A& aa){++ n;}int Getn(){return n;}
private:static int n;
};
int A::n = 0;
int main()
{A aa1;cout << aa1.Getn() << endl;return 0;
}
靜態成員函數
靜態成員函數的訪問方式如下
A::Getn()也是可以這樣訪問的,另外靜態成員變量也同理
靜態成員函數沒有this指針
靜態成員函數與普通的成員函數不同點在于靜態成員函數沒有this指針,所以不能訪問非靜態成員變量或者函數
class A
{
public:A(){++n;}A(const A& aa){++ n;}static int Getn(){a++;return n;}
private:static int n;int a;
};
int A::n = 0;
int main()
{A aa1;cout << A::n << endl;return 0;
}
特性
1. 靜態成員為所有類對象所共享,不屬于某個具體的對象,存放在靜態區
2. 靜態成員變量必須在類外定義,定義時不添加static關鍵字,類中只是聲明
3. 類靜態成員即可用 類名::靜態成員 或者 對象.靜態成員 來訪問
4. 靜態成員函數沒有隱藏的this指針,不能訪問任何非靜態成員
5. 靜態成員也是類的成員,受public、protected、private 訪問限定符的限制
友元
友元提供了一種突破封裝的方式,有時提供了便利。但是友元會增加耦合度,破壞了封裝,所以
友元不宜多用。所以友元我們了解一下就行了
友元分為:友元函數和友元類
友元函數
問題:
現在嘗試去重載operator<<,然后發現沒辦法將operator<<重載成成員函數
因為cout的輸出流對象和隱含的this指針在搶占第一個參數的位置。this指針默認是第一個參數也就是左操作數了。
但是實際使用中cout需要是第一個形參對象,才能正常使用
所以要將operator<<重載成全局函數。但又會導致類外沒辦法訪問成員,此時就需要友元來解決。operator>>同理。
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常規調用// 因為成員函數第一個參數一定是隱藏的this,所以d1必須放在<<的左側ostream& operator<<(ostream& _cout){_cout << _year << "-" << _month << "-" << _day << endl;return _cout;}
private:int _year;int _month;int _day;
};
友元函數可以直接訪問類的私有成員,它是定義在類外部的普通函數,不屬于任何類,但需要在
類的內部聲明,聲明時需要加friend關鍵字。
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;
}
說明:
友元函數可訪問類的私有和保護成員,但不是類的成員函數
友元函數不能用const修飾(沒有this指針)
友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制
一個函數可以是多個類的友元函數
友元函數的調用與普通函數的調用原理相同
友元類
友元類的所有成員函數都可以是另一個類的友元函數,都可以訪問另一個類中的非公有成員。
友元關系是單向的,不具有交換性。
比如上述Time類和Date類,在Time類中聲明Date類為其友元類,那么可以在Date類中直接
訪問Time類的私有成員變量,但想在Time類中訪問Date類中私有的成員變量則不行。
友元關系不能傳遞
如果C是B的友元, B是A的友元,則不能說明C時A的友元。友元關系不能繼承
class Time
{friend class Date; // 聲明日期類為時間類的友元類,則在日期類中就直接訪問Time類
中的私有成員變量
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){// 直接訪問時間類私有的成員變量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t;
};
內部類
概念:如果一個類定義在另一個類的內部,這個內部類就叫做內部類。內部類是一個獨立的類,
它不屬于外部類,更不能通過外部類的對象去訪問內部類的成員。外部類對內部類沒有任何優越
的訪問權限。
注意:內部類就是外部類的友元類,內部類可以通過外部類的對象參數來訪問外部類中的所有成員
但是外部類不是內部類的友元。
特性:
1. sizeof(外部類)=外部類,和內部類沒有任何關系。
2. 內部類可以定義在外部類的public、protected、private都是可以的,且內部類受類域限制
3. 注意內部類可以直接訪問外部類中的static成員,不需要外部類的對象/類名(內部類是外部類的友元類)
特性1
class A
{
private:static int k;int h;
public:class B // B天生就是A的友元{public:void foo(const A& a){cout << k << endl;cout << a.h << endl;}};
};
int A::k = 1;
int main()
{A::B b;b.foo(A());cout << sizeof(A) << endl;return 0;
}
sizeof(A)的結果是4,可能很多人會覺得B在A里面的,所以sizeof(A)的結果是包含了B的空間的,但是事實上不是這樣的,如果要讓sizeof(A)的結果是算上B的空間大小的話應該像下面這段代碼這樣寫
class B
{
private:int _b1;
};
class A
{
private:static int k;int h;B _b;
}
此外類是不占用空間的,因為類只是一個聲明,而在定義的時候才會有空間,也就是說聲明只是說有這么一個東西,但是不會講空間分配給他,而定義則是讓這個東西真實的存在,并分配空間給他
特性2
內部類也是受訪問限定符和類域的限制
class A
{
public:class B{};
};
int main()
{A a;B b;return 0;
}
當我們用域作用限定符的時候就可以正常運行
class A
{
public:class B{};
};
int main()
{A a;A::B b;return 0;
}
但是當class B 在A的private中就會因為B是私有導致無法訪問
class A
{
private:class B{};
};
int main()
{A a;A::B b;return 0;
}
匿名對象
匿名對象就是沒有名字的對象,他的特點是生命周期只在當前一行
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" <<a<< endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{A aa1;A aa2(2);A();A(3);return 0;
}
拷貝對象時的一些編譯器優化
在傳參和傳返回值的過程中,一般編譯器會做一些優化,減少對象的拷貝,這個在一些場景下還
是非常有用的。
但是不同的編譯器優化程度是不同的,所以我們只需要簡單了解一下就可以了
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};
void f1(A aa)
{}
A f2()
{A aa;return aa;
}
int main()
{// 傳值傳參A aa1;f1(aa1);cout << endl;// 傳值返回f2();cout << endl;// 隱式類型,連續構造+拷貝構造->優化為直接構造f1(1);// 一個表達式中,連續構造+拷貝構造->優化為一個構造f1(A(2));cout << endl;// 一個表達式中,連續拷貝構造+拷貝構造->優化一個拷貝構造A aa2 = f2();cout << endl;// 一個表達式中,連續拷貝構造+賦值重載->無法優化aa1 = f2();cout << endl;return 0;
}