柚子快報激活碼778899分享:【C++】類和對象(下)
【C++】類和對象(下)
初始化列表構(gòu)造時的類型轉(zhuǎn)化static成員概念特性
友元友元函數(shù)友元類
內(nèi)部類匿名對象總結(jié)
初始化列表
在對類和對象有了基本的認(rèn)識之后,可以知道在創(chuàng)建對象的時候,編譯器通過調(diào)用構(gòu)造函數(shù),給對象中各個成員變量一個合適的初始值。
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
上面的代碼通過構(gòu)造函數(shù)賦予了成員變量新的值,但不能稱之為對對象中成員變量的初始化,構(gòu)造函數(shù)中的語句只能稱之為賦初值,而不能稱作初始化。
初始化只能初始化一次,而構(gòu)造函數(shù)體內(nèi)可以多次賦值。
初始化列表:以一個冒號開始,接著是一個以逗號分隔的數(shù)據(jù)或成員列表,每個成員變量后面跟著一個放在括號中的初始值或者表達(dá)式。
class Date
{
public:
Date(int year, int month, int day)
//對象的定義
:_year(year)
,_month(month)
,_day(day)
{}
private:
//對象的聲明
int _year;
int _month;
int _day;
};
int main(void)
{
Date d(2000,1,1);//對象的整體定義
return 0;
}
成員變量在類里面是聲明,在主函數(shù)(main)中會對成員變量進(jìn)行對象的整體定義,所有C++中規(guī)定了初始化列表,對象的成員定義的位置便是在初始化列表,在構(gòu)造函數(shù)體內(nèi)進(jìn)行的是成員變量的賦值。
【注意】 1.每一個成員變量在初始化列表中最多只能出現(xiàn)一次(初始化只能初始化一次)
成員變量可以在初始化列表不出現(xiàn),但是出現(xiàn)最多只能出現(xiàn)一次,否則就會報錯。
如果成員變量在初始化列表中不出現(xiàn),成員變量中的內(nèi)置類型不做處理,而自定義類型會調(diào)用其默認(rèn)的構(gòu)造函數(shù)。
2.類中包含以下成員,必須放在初始化列表位置進(jìn)行初始化。
引用成員變量
class B
{
public:
B(int& i)
:_i(i)
{}
private:
int& _i;
};
int main(void)
{
int i = 10;
B b1(i);
return 0;
}
當(dāng)類中存在引用成員變量時,必須在定義的時候初始化。 而在傳引用值時,需要預(yù)先在出函數(shù)中定義好一個變量,構(gòu)造函數(shù)接收參數(shù)時使用引用,如果只是傳遞一個值,在調(diào)用構(gòu)造函數(shù)時會建立一個臨時變量存儲這個值,當(dāng)構(gòu)造函數(shù)銷毀時,臨時變量也會銷毀,那么此時這個引用就會變成一種野引用。
const成員變量
class B
{
public:
B(int c)
:_c(c)
{}
private:
const int _c;
};
int main(void)
{
B bb1(10);
return 0;
}
當(dāng)類中存在const修飾成員變量時,必須在定義的時候初始化。 被const修飾的變量只有一次初始機(jī)會,此后不可以被修改,所有必須在初始化列表的定義。
自定義類型(且該類型沒有默認(rèn)的構(gòu)造函數(shù))
class A
{
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
class B
{
public:
B(int aa)
:_aa(aa)
{}
private:
A _aa;
};
int main(void)
{
B bb(1);
return 0;
}
當(dāng)自定義類型沒有默認(rèn)構(gòu)造函數(shù)(全缺省、無參、編譯器自定生成),需要在初始化列表定義,由默認(rèn)構(gòu)造函數(shù),也可以在初始化列表進(jìn)行定義。
有默認(rèn)構(gòu)造函數(shù),默認(rèn)構(gòu)造函數(shù)的參數(shù)為缺省值,需要先考慮初始化列表,初始化列表可以看作是成員變量定義的地方。
【注意】 初始化只是構(gòu)造函數(shù)的一部分,構(gòu)造函數(shù)里面初始化賦值有倆種:第一種是在函數(shù)體內(nèi)賦值,另一種是初始化列表定義。
也就是說,并不是初始化列表可以滿足構(gòu)造函數(shù)的問題,在構(gòu)造函數(shù)內(nèi)部可以進(jìn)行一些其他的工作,例如內(nèi)存的開辟,內(nèi)存開辟后的判斷,多維數(shù)組的建立等等。
class Stack
{
public:
Stack(int capacity)
:_capacity(capacity)
,_size(0)
{
_a = (int*)malloc(sizeof(int) * capacity);
if (_a == nullptr)
{
perror("malloc fail");
return;
}
}
private:
int* _a;
int _capacity;
int _size;
};
3.盡量使用初始化列表初始化,因為不管你是否使用初始化列表,對于自定義類型成員變量,一定會先使用初始化列表。
4.成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后順序無關(guān)。
class A
{
public:
A(int a)
:_a2(a)
,_a1(_a2)
{}
private:
int _a1;
int _a2;
};
這段錯誤代碼就演示了在初始化列表中先定義_a1時,使用_a2的值賦給_a1,由于_a2還未定義,所以_a1會出現(xiàn)隨機(jī)值。
構(gòu)造時的類型轉(zhuǎn)化
class A
{
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
int main(void)
{
A aa1 = 2;
return 0;
}
對對象初始化時,將一個整數(shù)類型賦值給自定義類型,這種方式被稱為隱式類型轉(zhuǎn)換,會先通過整數(shù)構(gòu)造一個自定義類型的臨時變量,臨時變量在拷貝構(gòu)造給需要構(gòu)造的對象——>這種過程直接會被編譯器有優(yōu)化為直接構(gòu)造,不通過拷貝構(gòu)造。 在同一個表達(dá)式中,連續(xù)的構(gòu)造編譯器一般會優(yōu)化。
這段代碼報錯的原因是,整數(shù)int類型在構(gòu)造一個自定義類型的臨時變量時,由于臨時變量具有常性,所以在將臨時變量拷貝給自定義類型時,臨時變量會釋放,引用會變成野引用,所以直接使用引用是不可以的。
可以使用const來使用引用,因為const引用不能被修改。
使用explicit關(guān)鍵字修飾構(gòu)造函數(shù),將會禁止構(gòu)造函數(shù)的隱式住轉(zhuǎn)化。
static成員
概念
聲明為static的類成員稱為類的靜態(tài)成員,用static修飾的成員變量稱為靜態(tài)成員變量。用static修飾的成員函數(shù)稱之為靜態(tài)成員函數(shù)。
靜態(tài)成員變量一定要在類外進(jìn)行初始化。
例如:檢查程序中有多少個對象正在使用
//使用全局變量
int _count = 0;
class A
{
public:
A(int a)
:_a(a)
{
++_count;
}
A(const A& a)
{
++_count;
}
~A()
{
--_count;
}
private:
int _a;
};
void Fun1()
{
A aa3(2);
cout << __LINE__ << ":" << _count << endl;
}
int main(void)
{
A aa1(1);
cout << __LINE__ << ":" << _count << endl;
A aa2(aa1);
cout << __LINE__ << ":" << _count << endl;
Fun1();
cout << __LINE__ << ":" << _count << endl;
return 0;
}
此代碼使用全局變量來測試,但是全局變量的劣勢是任何地方都可以將其修改。
類中除了可以定義成員變量,同時也可以定義靜態(tài)成員變量。
class A
{
public:
A(int a)
:_a(a)
{
++_count;
}
A(const A& a)
{
++_count;
}
~A()
{
--_count;
}
static int GetCount()
{
return _count;
}
private:
int _a;
static int _count;
};
int A::_count = 0;
void Fun1()
{
A aa3(2);
cout << __LINE__ << ":" << A::GetCount() << endl;
}
int main(void)
{
A aa1(1);
cout << __LINE__ << ":" << A::GetCount() << endl;
A aa2(aa1);
cout << __LINE__ << ":" << A::GetCount() << endl;
Fun1();
cout << __LINE__ << ":" << A::GetCount() << endl;
return 0;
}
對于成員變量與靜態(tài)成員變量的區(qū)別是,成員變量屬于每一個類對象,每一個對象中都會有成員變量,而靜態(tài)成員變量只屬于類,屬于類中每一個類對象共享的變量,存儲在靜態(tài)區(qū),其聲明周期是全局的。
靜態(tài)成員變量只在全局位置,類的外面定義,由于靜態(tài)成員變量在類中被封裝后變成私有,所以靜態(tài)成員函數(shù)不可以被訪問,所以可以使用類中公共的成員函數(shù)。
class A
{
public:
A(int a)
:_a(a)
{
++_count;
}
A(const A& a)
{
++_count;
}
~A()
{
--_count;
}
int GetCount()
{
return _count;
}
private:
int _a;
static int _count;
};
int A::_count = 0;
void Fun1()
{
A aa3(2);
cout << __LINE__ << ":" << aa3.GetCount() << endl;
}
int main(void)
{
A aa1(1);
cout << __LINE__ << ":" << aa1.GetCount() << endl;
A aa2(aa1);
cout << __LINE__ << ":" << aa1.GetCount() << endl;
Fun1();
cout << __LINE__ << ":" << aa1.GetCount() << endl;
return 0;
}
這種方式是無法對沒有對象的程序進(jìn)行調(diào)用函數(shù)。
所以也可以使用靜態(tài)成員函數(shù),靜態(tài)成員函數(shù)沒有this指針,指定類域或者訪問限定符就可以訪問靜態(tài)成員函數(shù),靜態(tài)成員函數(shù)中不能使用非靜態(tài)成員變量,因為沒有this指針。
【注意】由于靜態(tài)成員變量無法進(jìn)入構(gòu)造,所以無法通過初始化列表定義,所以在聲明靜態(tài)成員變量時,無法給缺省值。
鏈接:使用static計算1+2+3……+n
class Sum
{
public:
Sum()
{
_sum += _num;
++_num;
}
static int GetSum()
{
return _sum;
}
private:
static int _num;
static int _sum;
};
int Sum::_num = 1;
int Sum::_sum = 0;
class Solution {
public:
int Sum_Solution(int n)
{
Sum arr[n];
return Sum::GetSum();
}
};
設(shè)計一個類只能在棧上創(chuàng)建對象
class A
{
public:
static A GetStackObj(int a)
{
A a1(a);
return a1;
}
private:
A(int a1)
:_a1(a1)
{}
int _a1;
};
int main(void)
{
A a1 = A::GetStackObj();
return 0;
}
特性
1.靜態(tài)成員為所有類對象所共享,不屬于某個具體的對象,存放在靜態(tài)區(qū) 2.靜態(tài)成員變量必須在類外面定義,定義時不添加static關(guān)鍵字,在類里面只是聲明 3.類靜態(tài)成員即可用 類名::靜態(tài)成員 或者 對象.靜態(tài)成員 來訪問 4.靜態(tài)成員函數(shù)沒有隱藏的this指針,不能訪問任何非靜態(tài)成員 5.靜態(tài)成員也是類的成員,受public,protected,privated的影響 6.非靜態(tài)成員函數(shù)可以調(diào)用靜態(tài)成員函數(shù),原因是靜態(tài)成員函數(shù)沒有this指針,不構(gòu)成影響 7.靜態(tài)成員函數(shù)不能調(diào)用非靜態(tài)成員函數(shù),是因為非靜態(tài)成員函數(shù)調(diào)用需要this指針,而靜態(tài)成員函數(shù)沒有this指針
友元
友元提供了一種突破封裝的方式,有時可以利用友元提供方便,但使用友元會增加耦合度(關(guān)聯(lián)度更加緊密),可能會破壞封裝。 友元使用關(guān)鍵字friend 友元可以分為:友元函數(shù)與友元類。
友元函數(shù)
友元函數(shù)可以直接訪問類的私有成員,它是定義在類外部的普通函數(shù),不屬于任何類,但需要在類的內(nèi)部聲明,聲明時需要加friend關(guān)鍵字。
以流插入舉例:
class Date
{
friend ostream& operator<<(ostream& out, Date& d);
public:
Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, Date& d)
{
out << d._year << "/" << d._month << "/" << d._day;
return out;
}
觀察這段代碼,無法將operator<<放在類中成為重載成員函數(shù),是因為cout的輸出流對象和隱含的this指針在搶占第一個參數(shù)的位置,this指針默認(rèn)是第一個參數(shù),也就是左操作數(shù)。
d << cout;
但是實際使用中cout需要是第一個形參對象,才能正常使用,可以將operator重載成全局函數(shù),但是此時又會導(dǎo)致類外沒辦法訪問成員,此時就需要友元來解決。
【注意】 1.友元函數(shù)可以訪問類的私有和保護(hù)成員,但是不是類的成員函數(shù) 2.友元函數(shù)不能使用const修飾 3.友元函數(shù)可以哎類定義的任何地方聲明,不受類訪問限定符限制 4.一個函數(shù)可以是多個類的友元函數(shù) 5.友元函數(shù)的調(diào)用與普通的調(diào)用原理相同
友元類
class Date
{
friend class Time;
public:
Date(int year = 2000, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
class Time
{
public:
Time(int hour, int minute, int second)
:_hour(hour)
,_minute(minute)
,_second(second)
{}
void Print()
{
cout << _d._year << " " << _d._month << " " << _d._day << " "
<< _hour << " " << _minute << " " << _second;
}
private:
int _hour;
int _minute;
int _second;
Date _d;
};
int main(void)
{
Time T1(2, 23, 15);
T1.Print();
return 0;
}
友元類的所有成員函數(shù)都可以是另一個類的友元函數(shù),都可以訪問另一個類中的非公有成員。
友元函數(shù)是單向的,不具有交換性。友元關(guān)系不能傳遞。 例如:如果C是B的友元,B是A的友元,則不能說明C是A的友元。友元關(guān)系不能繼承。
內(nèi)部類
概念·:如果一個類定義在另一個類的內(nèi)部,這個內(nèi)部類就叫做內(nèi)部類。內(nèi)部類是一個獨立的類,它不屬于外部類,更不能通過外部類的對象去訪問內(nèi)部類的成員。外部類對內(nèi)部類沒有任何優(yōu)越的訪問權(quán)限。
【特性】 1.內(nèi)部類可以定義在外部類的public、protected、private都是可以的,其效果不同。 2.注意內(nèi)部類可以直接訪問外部類中的static成員,不需要外部類的對象或者類名 3.sizeof(外部類)==外部類,和內(nèi)部類沒有任何關(guān)系。 4.sizeof(類)時,里面的靜態(tài)變量不計算,是因為靜態(tài)變量沒有存儲在對象里面。 5.內(nèi)部類天生是外部類的友元。
class A
{
public:
class B
{
public:
B()
:_b(2)
{}
void Print(const A& a)
{
cout << a._a << _b << endl;
}
private:
int _b;
};
A()
:_a(1)
{}
private:
int _a;
};
int main()
{
A a;
A::B b;
b.Print(a);
return 0;
}
【注意】 內(nèi)部類就是外部類的友元類,同友元類一樣,內(nèi)部類可以通過外部類的對象參數(shù)來訪問外部類中的所有成員,但外部類不是內(nèi)部類的友元。
匿名對象
有名對象
A aa1(1);
匿名對象
A(2);
有名對象調(diào)用調(diào)用函數(shù)
Date d1;
d1.Print();
匿名函數(shù)調(diào)用對象
Date().Print();
有名對象的生命周期在當(dāng)前函數(shù)局部域,匿名對象的生命周期在當(dāng)前行。
匿名對象與有名對象的區(qū)別僅僅是匿名無名,而有名有名。
匿名對象與臨時對象相似,具有常性。
//錯誤代碼
A& a1 = A(1);
//正確代碼
const A& a2 = A(1);
const引用延長了匿名對象的生命周期,生命周期在當(dāng)前函數(shù)局部域。
同時,同一行一個表達(dá)式中連續(xù)的構(gòu)造+拷貝構(gòu)造會進(jìn)行優(yōu)化。
總結(jié)
在類和對象階段,需要體會到,類是對某一類實體(對象)來進(jìn)行描述的,描述該對象具有哪些屬性,哪些方法,描述完成后就形成了一種新的自定義類型,才有該自定義類型就可以實例化具體的對象。
柚子快報激活碼778899分享:【C++】類和對象(下)
好文推薦
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。