柚子快報(bào)邀請(qǐng)碼778899分享:C++之類與對(duì)象(完結(jié)撒花篇)
柚子快報(bào)邀請(qǐng)碼778899分享:C++之類與對(duì)象(完結(jié)撒花篇)
目錄
前言
1.再探構(gòu)造函數(shù)
2.類型轉(zhuǎn)換
3.static成員
4.?友元
5.內(nèi)部類
6.匿名對(duì)象
7.對(duì)象拷貝時(shí)的編譯器優(yōu)化
結(jié)束語(yǔ)
前言
在前面的博客中,我們對(duì)類的默認(rèn)成員函數(shù)都有了一定了解,同時(shí)實(shí)現(xiàn)了一個(gè)日期類對(duì)所學(xué)的沒內(nèi)容進(jìn)行擴(kuò)展延伸,本節(jié)我們將對(duì)類與對(duì)象進(jìn)行大致的最終學(xué)習(xí)。
1.再探構(gòu)造函數(shù)
? 之前實(shí)現(xiàn)構(gòu)造函數(shù)時(shí),初始化成員變量
主要使用函數(shù)體內(nèi)賦值,構(gòu)造函數(shù)初始化
還有一種方式,就是初始化列表,初始化列表的使用方式是以一個(gè)冒號(hào)開始,接著是一個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表,每個(gè)"成員變量"后面跟一個(gè)放在括號(hào)中的初始值或表達(dá)式。
? 每個(gè)成員變量在初始化列表中
只能出現(xiàn)一次,語(yǔ)法理解上初始化列表可以認(rèn)為是每個(gè)成員變量定義初始化的地方。
? 引用成員變量,const成員變量,沒有默認(rèn)構(gòu)造的類類型變量,必須放在初始化列表位置進(jìn)行初始化,否則會(huì)編譯報(bào)錯(cuò)。
#include
using namespace std;
class Time {
public:
Time(int hour=1)
: _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”: 沒有合適的默認(rèn)構(gòu)造函數(shù)可?
// error C2530 : “Date::_ref” : 必須初始化引?
// error C2789 : “Date::_n” : 必須初始化常量限定類型的對(duì)象
}
void Print() const{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
Time _t; // 沒有默認(rèn)構(gòu)造
int& _ref; // 引?
const int _n; // const
};
int main() {
int x = 1;
Date d1(x);
d1.Print();
return 0;
}
上述代碼是修改后的正確代碼展示
? C++11支持在成員變量聲明的位置給缺省值,這個(gè)缺省值主要是給沒有顯?在初始化列表初始化的成員使用的。
?
盡量使用初始化列表初始化,因?yàn)槟切┎辉诔跏蓟斜沓跏蓟某蓡T也會(huì)走初始化列表,如果這個(gè)成員在聲明位置給了缺省值,初始化列表會(huì)用這個(gè)缺省值初始化。如果你沒有給缺省值,對(duì)于沒有顯示在初始化列表初始化的內(nèi)置類型成員是否初始化取決于編譯器,C++并沒有規(guī)定。對(duì)于沒有顯示在初始化列表初始化的自定義類型成員會(huì)調(diào)用這個(gè)成員類型的默認(rèn)構(gòu)造函數(shù),如果沒有默認(rèn)構(gòu)造會(huì)編譯錯(cuò)誤。
? 初始化列表中按照成員變量在類中聲明順序進(jìn)行初始化,跟成員在初始化列表出現(xiàn)的的先后順序無關(guān)。建議聲明順序和初始化列表順序保持一致。
#include
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:
// 注意這?不是初始化,這?給的是缺省值,這個(gè)缺省值是給初始化列表的
// 如果初始化列表沒有顯?初始化,默認(rèn)就會(huì)?這個(gè)缺省值初始化
int _year = 1;
int _month = 1;
int _day;
Time _t = 1;
const int _n = 1;
int* _ptr = (int*)malloc(12);
};
int main()
{//對(duì)象定義
Date d1;
d1.Print();
return 0;
}
?補(bǔ)充題目
下面程序的運(yùn)行結(jié)果是什么(D)
A. 輸出 1 1? ? ?B. 輸出 2 2? ? ?C. 編譯報(bào)錯(cuò)
D. 輸出 1 隨機(jī)值? ? ?E. 輸出 1 2? ? F. 輸出 2 1
#include
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();
}
_a2 和 _a1 的初始化順序不符合它們?cè)陬愔新暶鞯捻樞?。這將導(dǎo)致 _a2 使用未初始化的 _a1 值,所以輸出的_a2是個(gè)隨機(jī)值
2.類型轉(zhuǎn)換
C++ 支持內(nèi)置類型(如 int、float 等)隱式轉(zhuǎn)換為類類型對(duì)象,只要類中定義了一個(gè)接受該內(nèi)置類型作為參數(shù)的構(gòu)造函數(shù)。這種構(gòu)造函數(shù)通常稱為單參數(shù)構(gòu)造函數(shù),能夠允許編譯器在需要時(shí)自動(dòng)創(chuàng)建對(duì)象。
#include
using namespace std;
class MyClass {
public:
// 單參數(shù)構(gòu)造函數(shù),接受一個(gè) int 類型
MyClass(int value) : _value(value) {
cout << "MyClass constructed with value: " << _value << endl;
}
void Print() const {
cout << "Value: " << _value << endl;
}
private:
int _value;
};
int main() {
MyClass obj = 10; // 隱式轉(zhuǎn)換,從 int 到 MyClass
obj.Print(); // 輸出: Value: 10
MyClass anotherObj(20); // 顯式構(gòu)造
anotherObj.Print(); // 輸出: Value: 20
return 0;
}
?
注意事項(xiàng)
隱式轉(zhuǎn)換的風(fēng)險(xiǎn):
盡管隱式轉(zhuǎn)換很方便,但可能會(huì)導(dǎo)致代碼的可讀性降低,尤其是在較大的代碼庫(kù)中。為了避免不必要的隱式轉(zhuǎn)換,可以將構(gòu)造函數(shù)聲明為?explicit,防止不小心的隱式轉(zhuǎn)換:
class MyClass {
public:
explicit MyClass(int value)
: _value(value) {}
// ...
};
多重構(gòu)造:
如果類中有多個(gè)構(gòu)造函數(shù),確保它們能夠明確區(qū)分,以避免二義性的問題。
3.static成員
? 用
static修飾的成員變量,稱之為靜態(tài)成員變量,靜態(tài)成員變量一定要在類外進(jìn)行初始化。
? 靜態(tài)成員變量為
所有類對(duì)象所共享,不屬于某個(gè)具體的對(duì)象,不存在對(duì)象中,存放在靜態(tài)區(qū)。
? 用static修飾的成員函數(shù),稱之為靜態(tài)成員函數(shù),靜態(tài)成員函數(shù)沒有this指針。
? 靜態(tài)成員函數(shù)中可以訪問其他的靜態(tài)成員,但是不能訪問非靜態(tài)的,因?yàn)闆]有this指針。
? 非靜態(tài)的成員函數(shù),可以訪問任意的靜態(tài)成員變量和靜態(tài)成員函數(shù)。
? 突破類域就可以訪問靜態(tài)成員,可以通過類名::靜態(tài)成員 或者 對(duì)象.靜態(tài)成員 來訪問靜態(tài)成員變量和靜態(tài)成員函數(shù)。
? 靜態(tài)成員也是類的成員,受public、protected、private 訪問限定符的限制。
? 靜態(tài)成員變量
不能在聲明位置給缺省值初始化,因?yàn)槿笔≈凳莻€(gè)構(gòu)造函數(shù)初始化列表的,靜態(tài)成員變量不屬于某個(gè)對(duì)象,不走構(gòu)造函數(shù)初始化列表。
#include
using namespace std;
class A {
public:
A() {
++_count;
}
A(const A& count) {
++_count;
}
~A() {
--_count;
}
static int getcount() {
return _count;
}
private:
//類里面聲明
static int _count;
};
int A::_count = 520;
int main() {
cout << A::getcount() << endl; // 輸出:520
A t1; // _count 增加到 521
A t2(t1); // _count 增加到 522
cout << A::getcount() << endl; // 輸出:522
// 此時(shí) t1 和 t2 仍然存在
cout << t1.getcount() << endl;//522
cout << t2.getcount() << endl;//522
{
A t3(t1); // _count 增加到 523
cout << A::getcount() << endl; // 輸出:523
} // t3 超出作用域, _count 減少到 522
cout << A::getcount() << endl; // 輸出:522
return 0;
}
題目練習(xí)
設(shè)已經(jīng)有A,B,C,D 4個(gè)類的定義,程序中A,B,C,D構(gòu)造函數(shù)調(diào)?順序?yàn)椋浚‥)
設(shè)已經(jīng)有A,B,C,D 4個(gè)類的定義,程序中A,B,C,D析構(gòu)函數(shù)調(diào)?順序?yàn)??(B)
C c;
int main() {
A a;
B b;
static D d;
return 0;
}
A:D B A C? ? ? ? ? ? ?B:B A D C? ? ? ? ? ? C:C D B A
D:A B D C? ? ? ? ? ? ?E:C A B D? ? ? ? ? ? F:C D A B
在全局或局部作用域中,構(gòu)造函數(shù)的調(diào)用順序如下:
全局和靜態(tài)對(duì)象的構(gòu)造:在程序啟動(dòng)時(shí),全局對(duì)象(如果有)和靜態(tài)對(duì)象會(huì)首先被構(gòu)造。局部對(duì)象的構(gòu)造:然后,當(dāng)程序進(jìn)入?main()?函數(shù)時(shí),局部對(duì)象的構(gòu)造按定義順序調(diào)用。
析構(gòu)函數(shù)的調(diào)用順序與構(gòu)造函數(shù)的順序相反。析構(gòu)函數(shù)會(huì)在對(duì)象的生命周期結(jié)束時(shí)被調(diào)用,順序如下:
局部對(duì)象的析構(gòu):當(dāng)程序退出?main()?函數(shù)時(shí),局部對(duì)象按定義的相反順序析構(gòu)。全局和靜態(tài)對(duì)象的析構(gòu):在?main()?函數(shù)結(jié)束后,全局對(duì)象和靜態(tài)對(duì)象會(huì)被析構(gòu)。
4.?友元
? 友元提供了一種突破類訪問限定符封裝的方式,友元分為:
友元函數(shù)和友元類,在函數(shù)聲明或者類聲明的前面
加friend,并且把友元聲明放到一個(gè)類的里面。
? 外部友元函數(shù)可訪問類的私有和保護(hù)成員,友元函數(shù)僅僅是一種聲明,他不是類的成員函數(shù)。
? 友元函數(shù)可以在類定義的任何地方聲明,不受類訪問限定符限制。
?
一個(gè)函數(shù)可以是多個(gè)類的友元函數(shù)。
?
友元類中的成員函數(shù)都可以是另一個(gè)類的友元函數(shù),都可以訪問另一個(gè)類中的私有和保護(hù)成員。
? 友元類的關(guān)系是單向的,不具有交換性,比如A類是B類的友元,但是B類不是A類的友元。
?
友元類關(guān)系不能傳遞,如果A是B的友元, B是C的友元,但是A不是B的友元。
? 有時(shí)提供了便利。但是友元會(huì)增加耦合度,破壞了封裝,所以友元不宜多用。
#include
using namespace std;
class B;//前置聲明
class A {
friend void func(const A& a, const B& b);
private:
int _a = 520;
int _b = 1314;
};
class B {
friend void func(const A& a, const B& b);
private:
int _a = 1314;
int _b = 520;
};
void func(const A& a, const B& b) {
cout << a._a << endl;
cout << b._b << endl;
}
int main() {
A a;
B b;
func(a, b);
return 0;
}
#include
using namespace std;
class A
{
// 友元聲明
friend class B;
private:
int _a1 = 520;
int _a2 = 1314;
};
class B
{
public:
void func1(const A& aa)
{
cout << aa._a1 << endl;
cout << _b2 << endl;
}
void func2(const A& aa)
{
cout << aa._a2 << endl;
cout << _b1 << endl;
}
private:
int _b1 = 520;
int _b2 = 1314;
};
int main()
{
A aa;
B bb;
bb.func1(aa);
bb.func2(aa);
return 0;
}
5.內(nèi)部類
? 如果一個(gè)類定義在另一個(gè)類的內(nèi)部,這個(gè)內(nèi)部類就叫做內(nèi)部類。內(nèi)部類是一個(gè)獨(dú)立的類,跟定義在全局相比,他只是受外部類類域限制和訪問限定符限制,所以外部類定義的對(duì)象中不包含內(nèi)部類。
?
內(nèi)部類默認(rèn)是外部類的友元類。
? 內(nèi)部類本質(zhì)也是一種封裝,當(dāng)A類跟B類緊密關(guān)聯(lián),A類實(shí)現(xiàn)出來主要就是給B類使用,那么可以考慮把A類設(shè)計(jì)為B的內(nèi)部類,
如果放到private/protected位置,那么A類就是B類的專屬內(nèi)部類,其他地方都用不了。
#include
using namespace std;
class A {
private:
static int _a; // 靜態(tài)成員
int _b; // 非靜態(tài)成員
public:
class B {
public:
void print(const A& a) {
cout << _a << endl; // 訪問靜態(tài)成員
cout << a._b << endl; // 訪問非靜態(tài)成員
}
};
};
int A::_a = 520; // 靜態(tài)成員初始化
int main() {
cout << "A類的大?。? << sizeof(A) << endl; // 輸出 A 類的大小
A::B b; // 創(chuàng)建 B 類的對(duì)象
A aa; // 創(chuàng)建 A 類的對(duì)象
b.print(aa); // 調(diào)用 print 函數(shù)
return 0;
}
6.匿名對(duì)象
?
用類型(實(shí)參) 定義出來的對(duì)象叫做匿名對(duì)象,相比之前我們定義的 類型 對(duì)象名(實(shí)參) 定義出來的 叫有名對(duì)象
?
匿名對(duì)象生命周期只在當(dāng)前一行,一般臨時(shí)定義一個(gè)對(duì)象當(dāng)前用一下即可,就可以定義匿名對(duì)象。
#include
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;
}
};
bool myfunction(int i, int j) { return (i > j); }
int main()
{
A aa1; //有名對(duì)象
// 不能這么定義對(duì)象,因?yàn)榫幾g器無法識(shí)別下面是一個(gè)函數(shù)聲明,還是對(duì)象定義
//A aa2();
// 生命周期只在當(dāng)前一行
A(); // 匿名對(duì)象
A(1);
Solution st;
cout << st.Sum_Solution(10) << endl;
// 為了更方便
cout << Solution().Sum_Solution(10) << endl;
return 0;
}
7.對(duì)象拷貝時(shí)的編譯器優(yōu)化
? 現(xiàn)代編譯器會(huì)為了盡可能提高程序的效率,在不影響正確性的情況下會(huì)盡可能減少一些傳參和傳參過程中可以省略的拷貝。
? 如何優(yōu)化C++標(biāo)準(zhǔn)并沒有嚴(yán)格規(guī)定,各個(gè)編譯器會(huì)根據(jù)情況自行處理。當(dāng)前主流的相對(duì)新一點(diǎn)的編譯器對(duì)于連續(xù)一個(gè)表達(dá)式步驟中的連續(xù)拷貝會(huì)進(jìn)行合并優(yōu)化,有些更新更"激進(jìn)"的編譯還會(huì)進(jìn)行跨行跨表達(dá)式的合并優(yōu)化。
#include
using namespace std;
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;
}
void Print()
{
cout << "A::Print->" << _a << endl;
}
A& operator++()
{
_a += 100;
return *this;
}
private:
int _a ;
};
void f1(A aa)
{}
A f2()
{
A aa(1);
++aa;
cout << "##########" << endl;
return aa;
}
int main() {
A aa1 ;
aa1.Print();
//const A& aa2 = 2;
//A aa3(aa2);
//A aa1;
//f1(aa1);
//cout << endl;
// 隱式類型,連續(xù)構(gòu)造+拷?構(gòu)造->優(yōu)化為直接構(gòu)造
//f1(1);
// ?個(gè)表達(dá)式中,連續(xù)構(gòu)造+拷?構(gòu)造->優(yōu)化為?個(gè)構(gòu)造
//f1(A(2));
//cout << endl;
//cout << "***********************************************" << endl;
// 傳值返回
// 返回時(shí)?個(gè)表達(dá)式中,連續(xù)拷?構(gòu)造+拷?構(gòu)造->優(yōu)化?個(gè)拷?構(gòu)造 (vs2019)
// ?些編譯器會(huì)優(yōu)化得更厲害,進(jìn)?跨?合并優(yōu)化,直接變?yōu)闃?gòu)造。(vs2022)
//f2();
//cout << endl;
// 返回時(shí)?個(gè)表達(dá)式中,連續(xù)拷?構(gòu)造+拷?構(gòu)造->優(yōu)化?個(gè)拷?構(gòu)造 (vs2019)
// ?些編譯器會(huì)優(yōu)化得更厲害,進(jìn)?跨?合并優(yōu)化,直接變?yōu)闃?gòu)造。(vs2022)
// A aa2 = f2();
// cout << endl;
// ?個(gè)表達(dá)式中,連續(xù)拷?構(gòu)造+賦值重載->?法優(yōu)化
aa1 = f2();
cout << endl;
A ret = f2();
ret.Print();
cout << "*********" << endl << endl;
//
return 0;
}
結(jié)束語(yǔ)
本節(jié)內(nèi)容到此結(jié)束,類與對(duì)象的學(xué)習(xí)也暫時(shí)告別一段落了,希望接下來繼續(xù)能和大家探討C++的學(xué)習(xí),最后呢,感謝各位友友的支持,講解不足之處也希望大家多多包涵?。?!?
柚子快報(bào)邀請(qǐng)碼778899分享:C++之類與對(duì)象(完結(jié)撒花篇)
推薦文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。