C++知識點---類和物件
1. 類和物件
類:和結構體類似,存在資料(變數),還有方法(函式),即類包含成員變數和成員函式
物件:類的例項,將物件作為程式的基本單元,將程式和資料封裝其中,以提高軟體的重用性、靈活性和擴充套件性。
2. 類和物件的特點
類:具有封裝性,將變數和函式封裝到類當中,類的訪問限定符(public、private、protected)體現了面向物件的封裝性
物件:封裝,繼承,多型
3. 具體例子
3.1 類的定義
//類內定義成員函式
class Person
{
public:
void Show()//可以直接從類外部訪問,作用域為:到下一個public,或者類結束
{
cout<<_name<<endl;
cout<<_sex<<endl;
cout<<_age<<endl;
}
private://不可直接從類外部訪問
char* _name;
char* _sex;
int _age;
}
//類外定義成員函式
class Person
{
public:
void Show();//可以直接從類外部訪問,作用域為:到下一個public,或者類結束
private://不可直接從類外部訪問
char * _name;
char* _sex;
int _age;
}
void Date::Show()//類外寫成員函式必須指明函式屬於哪一個類域
{
cout<<_name<<endl;
cout<<_sex<<endl;
cout<<_age<<endl;
}
3.2 例項化:
class Person
{
public:
void Show()
{
cout<<_name<<endl;
cout<<_sex<<endl;
cout <<_age<<endl;
}
private:
char* _name;
char* _sex;
int _age;
}
void Test()
{
Person P;//類的物件
p._name = "張三";
P._sex = "男";
P._age = 10;
}
4. 類的作用域:
成員函式和成員變數都在類的作用域內,類內部成員可直接訪問
類外定義成員,使用::表示在哪個作用域
例:
//類的宣告
class Person
{
Public:
void show();
private:
char* _name;
int _age;
}
//定義
Person::show()
{
cout<<_name<<endl;
cout<<_age<<endl;
}
5. 類的大小:
和結構體記憶體對齊規則一致
只取決於成員變數的大小,成員函式不佔物件空間(原因:將成員函式放在公共程式碼區,供大家使用)
空類的大小為1,開闢一個空間表示類的存在
記憶體對齊規則
- 第一個成員在與結構體變數偏移量為0的地址處。
- 其他成員變數要對齊到某個數字(對齊數)的整數倍的地址處。
對齊數 = 編譯器預設的一個對齊數 與 該成員大小的較小值。
VS中預設的值為8
Linux中的預設值為4- 結構體總大小為最大對齊數(每個成員變數除了第一個成員都有一個對齊數)的整數倍。
- 如果嵌套了結構體的情況,巢狀的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含巢狀結構體的對齊數)的整數倍。
6. 記憶體對齊的原因
由於存在記憶體訪問機制,每次訪問記憶體時從0號地址處連續讀取4位元組(或4位元組的整數倍);
如果不存在記憶體對齊,讀取一個變數時會讀取兩次或者三次甚至更多,反而降低效率
記憶體對齊的前提下,讀取一次即可提高效率
7. this指標
每個成員函式都有一個指標形參,它的名字是固定的,稱為this指標,this指標是隱式的。(建構函式比較特殊,沒有這個隱含this形參)
編譯器會對成員函式進行處理,在物件呼叫成員函式時,物件地址作實參傳遞給成員函式的第一個形參this指標。
this指標是成員函式隱含指標形參,是編譯器自己處理的,我們不能在成員函式的形參中新增this指標的引數定義,也不能在呼叫時
顯示傳遞物件的地址給this指標。
類的
void show();//原本成員函式
void show(Date* this);//編譯器處理的成員函式
8. 預設成員函式
8.1 建構函式
沒有返回值,可以有引數,沒有引數的建構函式被認為是預設建構函式,但預設建構函式只允許有一個
與類同名,可以有多個建構函式 ,可以過載, 沒有隱含的this形參
作用是將物件初始化,創造出物件時系統自動呼叫建構函式進行物件的例項化
例:
class Date
{
public:
//兩個預設引數只可取其一
Date();//(1)類內宣告
/*Date(int year=1900,int month=1,int day=1)//(2)類內定義
{
_year=year;
_month=month;
_day=day;
}*/
void show()
{
cout<<_year<<endl;
cout<<_month<<endl;
cout<<_day<<endl;
}
private:
int _month;
int _year;
int _day;
}
//類外定義:
Date::Date()
{
_year=1900;
_month=1;
_day=1;
}
注意:如果沒有定義建構函式,編譯器會自己生成預設預設建構函式(無參的建構函式),如果是成員變數是內建型別,預設建構函式不會初始化,如果成員變數是自定義型別,自動生成的預設建構函式呼叫自定義型別的建構函式進行初始化
例:
//自定義型別
class Time
{
public:
Time()//自定義型別的建構函式
{
cout<<_hour<<endl;
}
private:
int _hour;
}
class Date
{
public://無建構函式,生成預設的預設建構函式(無參的建構函式),什麼也不做(成員函式有自定義型別除外)
void show()
{
cout<<_year<<endl;
cout<<_month<<endl;
cout<<_day<<endl;
}
private:
int _month;
int _year;
int _day;//預設生成的建構函式不會對齊初始化
Time _t;//呼叫自定義型別的建構函式對其進行初始化
}
8. 2 解構函式
與類同名,並且與建構函式工作性質相反,故有~ 標識解構函式
當物件生命週期結束(即出了類的作用域),系統自動呼叫解構函式
不支援過載,完成清理工作(動態開闢),保證物件的初始化和銷燬
例:
class SeqList
{
public:
SeqList(size_t capacity=0)//建構函式
{
if(capacity > 0)
{
_a=(int*)malloc(sizeof(int)*capacity);
_capacity=capacity;
_size=0;
}
else
{
_a=NULL;
_capacity=_size=0
}
}
~SeqList()//解構函式
{
free(_a);
_size=_capacity=0;
}
private:
int* _a;
size_t _size;
size_t _capacity;
}
8.3 拷貝建構函式
引數是一個同類型的物件,使用同類物件進行初始化
拷貝建構函式使用引用
如果沒有定義拷貝建構函式,系統自動生成預設拷貝建構函式並進行初始化
例:
class Date
{
public:
Date(int year=1900,int month=1,int day=1)//預設引數
{
_year=year;
_month=month;
_day=day;
}
Date(const Date& d)//加引用,防止無限遞迴
{
_year=d._year;
_month=d._month;
_day=d._day;
}
void show()
{
cout<<_year<<endl;
cout<<_month<<endl;
cout<<_day<<endl;
}
private:
int _month;
int _year;
int _day;
}
8.4 運算子過載
作用:增強程式碼的可讀性
特徵:operator + 運算子(例:operator<)
不能過載的運算子: .* :: sizeof ?: :
兩種形態:成員函式(一個引數,還有一個隱含的this),全域性
例:
class Date
{
public:
Date(int year=1900,int month=1,int day=1);//預設引數
Date(const Date& d);//加引用,防止無限遞迴
void show();
bool operator==(const Date& d1,const Date& d2);//類內宣告
private:
int _month;
int _year;
int _day;
}
//全域性
bool Date::operator==(const Date& d1,const Date& d2)
{
return d1._year==d2._year
&& d1._month==d2.month
&& d1._day==d2._day;
}
//成員函式
class Date
{
public:
Date(int year=1900,int month=1,int day=1);//預設引數
Date(const Date& d);//加引用,防止無限遞迴
void show();
bool operator==(const Date& d1,const Date& d2);//成員函式
{
return d1._year==d2._year
&& d1._month==d2.month
&& d1._day==d2.day;
}
private:
int _month;
int _year;
int _day;
}