C++类class
一、定義
構(gòu)造函數(shù):在定義一個類對象時會自動調(diào)用,可用于實(shí)現(xiàn)一些功能,比如new一個內(nèi)存。
- 構(gòu)造函數(shù),沒有返回值也不寫void
- 函數(shù)名稱與類名相同
- 構(gòu)造函數(shù)可以有參數(shù),因此可以發(fā)生重載
- 程序在調(diào)用對象時候會自動調(diào)用構(gòu)造,無須手動調(diào)用,而且只會調(diào)用一次
?
析構(gòu)函數(shù):在類對象銷毀時執(zhí)行,可用于實(shí)現(xiàn)一些功能,比如delete一個內(nèi)存。
- 析構(gòu)函數(shù),沒有返回值也不寫void
- 函數(shù)名稱與類名相同,在名稱前加上符號 ~
- 析構(gòu)函數(shù)不可以有參數(shù),因此不可以發(fā)生重載
- 程序在對象銷毀前會自動調(diào)用析構(gòu),無須手動調(diào)用,而且只會調(diào)用一次
?
#include <iostream>
using namespace std;
class Person
{
public:Person() { cout << "無參構(gòu)造函數(shù)" << endl; }Person(int n_age) :age(n_age) { cout << "有參構(gòu)造函數(shù)" << endl; }Person(const Person& p) { age = p.age; cout << "復(fù)制構(gòu)造函數(shù)" << endl;}~Person() { cout << "析構(gòu)函數(shù)" << endl; }void show_age(int n_age){this->age = n_age;cout << n_age << endl;}
private:int age;
};int main()
{// 1、無參構(gòu)造函數(shù)Person p1;// 2、有參構(gòu)造函數(shù)Person p2(10);// 3、復(fù)制構(gòu)造函數(shù)Person p3(p2);p3.show_age(120);// 構(gòu)造函數(shù)的定義// 1、括號Person p4(10);// 2、顯式法Person p5 = Person(100);// 3、隱式法(當(dāng)類只有一個參數(shù)時,可以使用隱式法)Person p6 = 10;cout << "函數(shù)即將結(jié)束,開始析構(gòu)" << endl;return 0;
}
1.2 explicit關(guān)鍵字
可以取消類的隱式構(gòu)造
#include <iostream>
#include <string>
using namespace std;class Phone
{
public:Phone(string name) {this->phone_name = name;}Phone() {}~Phone() {}string phone_name;
};int main()
{string name = "huawei";Phone p = name;cout << p.phone_name << endl;return 0;
}
加入explicit關(guān)鍵字后可以取消隱式構(gòu)造
#include <iostream>
#include <string>
using namespace std;class Phone
{
public:explicit Phone(string name) {this->phone_name = name;}Phone() {}~Phone() {}string phone_name;
};int main()
{string name = "huawei";// 錯誤,此時不能隱式構(gòu)造Phone p = name;cout << p.phone_name << endl;return 0;
}
二、復(fù)制構(gòu)造函數(shù)
C++中拷貝構(gòu)造函數(shù)調(diào)用時機(jī)通常有三種情況
- 使用一個已經(jīng)創(chuàng)建完畢的對象來初始化一個新對象
- 值傳遞的方式給函數(shù)參數(shù)傳值
- 以值方式返回局部對象
#include <iostream>
using namespace std;
class Person
{
public:Person() { cout << "無參構(gòu)造函數(shù)" << endl; }Person(int n_age) :age(n_age) { cout << "有參構(gòu)造函數(shù)" << endl; }Person(const Person& p) { age = p.age; cout << "復(fù)制構(gòu)造函數(shù)" << endl;}~Person() { cout << "析構(gòu)函數(shù)" << endl; }void show_age(int n_age){this->age = n_age;cout << n_age << endl;}
private:int age;
};void test01(Person p1)
{cout << "類對象作為函數(shù)形參傳遞,調(diào)用復(fù)制構(gòu)造函數(shù)" << endl;return;
}
Person test02()
{Person p2(100);cout << "函數(shù)返回值為類對象時,調(diào)用復(fù)制構(gòu)造函數(shù)" << endl;return p2;
}int main()
{Person p(100);Person p1(p);test01(p);Person p2 = test02();return 0;
}
三、淺拷貝/深拷貝
當(dāng)類中含有指針類型成員變量時,需要進(jìn)行深拷貝:
3.1 淺拷貝
#include <iostream>
#include <math.h>
using namespace std;
class Person
{
public:Person() { }Person(int n_age,int n_height) { age = n_age;height = new int(n_height);}Person(const Person& p) { age = p.age; // 淺拷貝height = p.height;}~Person() { if (height != NULL){cout << "釋放內(nèi)存" << endl;delete height;}}void show_age(int n_age){this->age = n_age;cout << n_age << endl;}
private:int age;int* height;
};int main()
{Person p(23,160);Person p1(p);return 0;
}
淺拷貝中,p和p1的height指向同一個內(nèi)存,當(dāng)p釋放掉內(nèi)存中的數(shù)據(jù)之后,p1所指向的內(nèi)存中的數(shù)據(jù)為空,此時再釋放的話會報錯,因為這片內(nèi)存中的數(shù)據(jù)已經(jīng)不存在了。(注意,釋放的不是內(nèi)存,而是內(nèi)存中的數(shù)據(jù))
3.2 深拷貝
#include <iostream>
#include <math.h>
using namespace std;
class Person
{
public:Person() { }Person(int n_age,int n_height) { age = n_age;height = new int(n_height);}Person(const Person& p) { age = p.age; // 深拷貝height = new int(*(p.height));}~Person() { if (height != NULL){cout << "釋放內(nèi)存" << endl;delete height;}}void show_age(int n_age){this->age = n_age;cout << n_age << endl;}
private:int age;int* height;
};int main()
{Person p(23,160);Person p1(p);return 0;
}
四、類對象作為類成員
先調(diào)用成員類的構(gòu)造,然后是該類的構(gòu)造。析構(gòu)順序相反。
#include <iostream>
using namespace std;class Phone
{
public:Phone(string n_name) :phone_name(n_name) {cout << "Phone構(gòu)造函數(shù)" << endl;}Phone() {}~Phone() {cout << "Phone析構(gòu)函數(shù)" << endl;}
private:string phone_name;
};class Person
{
public:Person() {}Person(string n_name, string n_phone):m_name(n_name),m_phone(n_phone) {cout << "Person構(gòu)造函數(shù)" << endl;}~Person(){cout << "Person析構(gòu)函數(shù)" << endl;}
private:string m_name;Phone m_phone;
};
int main()
{Person person1("xiaoming", "huawei");return 0;
}
五、this指針
5.1 this指針使用的原因
在類中,非靜態(tài)成員變量屬于類對象,而非靜態(tài)成員函數(shù)為所有類對象共享,不屬于某個類對象:
#include <iostream>
#include <string>
using namespace std;class Phone
{
public:explicit Phone(string name) {this->phone_name = name;}Phone() {}~Phone() {}void test_func() { cout << "成員函數(shù)不占用類對象的內(nèi)存" << endl; }string phone_name;
};int main()
{string name = "huawei";// 錯誤,此時不能隱式構(gòu)造cout << sizeof(name) << endl;Phone p(name);// 可見類對象只占有一個string類型變量的內(nèi)存cout << sizeof(p) << endl;return 0;
}
5.2 this指針
每一個非靜態(tài)成員函數(shù)只會誕生一份函數(shù)實(shí)例,也就是說多個同類型的對象會共用一塊代碼
this指針的作用:
用于區(qū)分是哪個類對象調(diào)用了成員函數(shù)
this指針本質(zhì)上是一個指針常量,因此其指向的對象不能變,指向的對象的值可以變
this指針的用途
當(dāng)形參和成員變量同名時,可用this指針來區(qū)分
在類的非靜態(tài)成員函數(shù)中返回對象本身,可使用return *this
#include <iostream>
#include <string>
using namespace std;class Phone
{
public:explicit Phone(string name) {this->phone_name = name;}Phone() {}~Phone() {}void put_name(string phone_name) { // this->phone_name表示類的成員變量this->phone_name = phone_name;}// 使用this指針返回類自身Phone& get_phone_info(){this->phone_name += "10";return *this;}string phone_name;
};int main()
{Phone p;p.put_name("華為");cout << p.phone_name << endl;p.get_phone_info().get_phone_info().get_phone_info();cout << p.phone_name << endl;return 0;
}
六、友元
友元的目的就是讓一個函數(shù)或者類 訪問另一個類中私有成員
友元的三種實(shí)現(xiàn)
- 全局函數(shù)做友元
- 類做友元
- 成員函數(shù)做友元
6.1全局函數(shù)做友元
#include <iostream>
#include <string>
using namespace std;class room
{// 表明全局函數(shù)visit時友元,可以訪問私有變量friend void visit(room myroom);
public:room() {}room(string bedr) :bedroom(bedr) {}~room() {}
private:string bedroom;
};void visit(room myroom)
{cout << "go to " << myroom.bedroom << endl;
}int main()
{room myroom("dk's room");visit(myroom);return 0;
}
6.2類做友元
#include <iostream>
#include <string>
using namespace std;class room;
class person
{
public:person() {}person(string per) :name(per) {}~person() {}void visit();
private:string name;// 這里必須是指針,因為如果是變量的話// 編輯器不知道room類占了多少內(nèi)存// 也就沒法開辟內(nèi)存room* myroom;
};class room
{// person類時友元,可以訪問room類的私有變量friend class person;
public:room() {}~room() {}
private:string bedroom;
};void person::visit()
{this->myroom = new room;this->myroom->bedroom = "bedroom";// 此時person類中的room類對象myroom可以訪問room類中的私有變量cout << this->name << " is visiting " << this->myroom->bedroom << endl;return;
}int main()
{person myfriend("liming");myfriend.visit();return 0;
}
6.3成員函數(shù)做友元
#include <iostream>
#include <string>
using namespace std;class room;
class person
{
public:person() {}person(string per) :name(per) {}~person() {}void visit();
private:string name;// 這里必須是指針,因為如果是變量的話// 編輯器不知道room類占了多少內(nèi)存// 也就沒法開辟內(nèi)存room* myroom;
};class room
{// person類中的visit成員函數(shù)做友元,可以訪問room類的私有變量friend void person::visit();
public:room() {}~room() {}
private:string bedroom;
};void person::visit()
{this->myroom = new room;this->myroom->bedroom = "bedroom";// 此時person類中的room類對象myroom可以訪問room類中的私有變量cout << this->name << " is visiting " << this->myroom->bedroom << endl;return;
}int main()
{person myfriend("liming");myfriend.visit();return 0;
}
七、運(yùn)算符重載
7.1 加法運(yùn)算符重載
#include<iostream>
using namespace std;
class Person
{
public:Person() {}Person(int m_age):n_age(m_age) {}~Person() {}//相當(dāng)于一個成員函數(shù) +運(yùn)算符重載1 p1.operator+(p2)Person operator+(Person p){Person temp;temp.n_age = this->n_age + p.n_age;return temp;}
public:int n_age;
};// 相當(dāng)于一個函數(shù) operator+(p,age)
Person operator+(Person p,int age)
{Person temp;temp.n_age = p.n_age + age;return temp;
}int main()
{Person p1(20);Person p2(20);Person p3 = p1 + p2;cout << p3.n_age << endl;Person p4 = p1 + 20;cout << p4.n_age << endl;
}
7.2 輸出運(yùn)算符重載
#include<iostream>
using namespace std;
class Person
{
public:Person() {}Person(int m_age):n_age(m_age) {}~Person() {}public:int n_age;
};ostream& operator<<(ostream& out, Person p)
{out << "age is " << p.n_age << endl;return out;
}int main()
{Person p1(20);cout << p1 << "20" << endl;
}
7.3 函數(shù)調(diào)用運(yùn)算符重載(仿函數(shù))
- 函數(shù)調(diào)用運(yùn)算符 () 也可以重載
- 由于重載后使用的方式非常像函數(shù)的調(diào)用,因此稱為仿函數(shù)
- 仿函數(shù)沒有固定寫法,非常靈活
#include<iostream>
using namespace std;
class mycomp
{
public:void operator()(int a,int b){cout << a + b << endl;}
};int main()
{mycomp com1;com1(10, 20);return 0;
}
八、多態(tài)
多態(tài)是C++的重要特征,與封裝、繼承并稱為C++的三大特征
多態(tài)分為靜態(tài)多態(tài)、動態(tài)多態(tài)
靜態(tài)多態(tài):函數(shù)的地址的編譯時刻確定,主要包括 函數(shù)重載 和 運(yùn)算符重載
動態(tài)多態(tài):函數(shù)的地址在執(zhí)行時刻確定,通過 派生類 和 虛函數(shù)實(shí)現(xiàn)
?
動態(tài)多態(tài)滿足的條件
(1)存在繼承關(guān)系
(2)子類重寫父類的虛函數(shù)(函數(shù)名,返回值類型,形參完全相同)
注意,這里和繼承中的同名函數(shù)重寫不同。如果是同名函數(shù)的重寫,父類和子類的同名函數(shù)的地址在編譯階段就固定了。而多態(tài)中,父類中的虛函數(shù)的地址在編譯的時候是不確定的。
其類似于函數(shù)的重載
多態(tài)優(yōu)點(diǎn):代碼組織結(jié)構(gòu)清晰,可讀性強(qiáng),利于前期和后期的擴(kuò)展以及維護(hù)(不用去修改源碼,直接在子類中重寫虛函數(shù)即可)
?
多態(tài)使用條件:父類的指針或引用指向子類的對象
8.1 多態(tài)示例
8.1.1示例1
#include<iostream>
#include<string>
using namespace std;
class game
{
public:virtual void func(string name){cout << "I like playing " << name << endl;}
};class BH3:public game
{
public:BH3(string name) :n_name(name) {}void func(string name){cout << "I like playing " << this->n_name << endl;}string n_name;
};class YS:public game
{
public:YS(string name) :n_name(name) {}void func(string name){cout << "I like playing " << this->n_name << endl;}string n_name;
};void test_func(game& mygame)
{mygame.func("game");
}int main()
{BH3 bh3("崩壞3");test_func(bh3);YS ys("原神");test_func(ys);return 0;
}
8.1.2 示例2
#include<iostream>
using namespace std;
// 運(yùn)算器基類
class calculate
{
public:virtual int getresult(int x1,int x2){return 0;}
};// 加法運(yùn)算器
class addcalculate:public calculate
{
public:int getresult(int x1,int x2){return x1 + x2;}
};// 減法運(yùn)算器
class subcalculate :public calculate
{
public:int getresult(int x1, int x2){return x1 - x2;}
};
int main()
{// 構(gòu)建一個加法運(yùn)算器// 父類的指針或引用指向子類的對象calculate* cal1 = new addcalculate();cout << "加法結(jié)果" << cal1->getresult(10, 20) << endl;// 構(gòu)建一個減法運(yùn)算器calculate* cal2 = new subcalculate();cout << "減法結(jié)果" << cal2->getresult(10, 20) << endl;
}
8.2 純虛函數(shù)/抽象類
在多態(tài)中,父類中的虛函數(shù)一般不會使用,使用的是子類中重寫的虛函數(shù)。
所以,一般將父類的虛函數(shù)寫為純虛函數(shù),格式為:
virtual 返回值類型 函數(shù)名 (參數(shù)列表)= 0 ;
含有純虛函數(shù)的類稱為抽象類
抽象類的特點(diǎn):
子類必須重寫抽象類中的純虛函數(shù),否則子類也是抽象類
抽象類無法進(jìn)行初始化
#include<iostream>
using namespace std;
class Calculator
{
public:// 純虛函數(shù),此時Calculator為抽象類,無法進(jìn)行初始化virtual int calculate(int x1, int x2) = 0;
};class AddCalculator :public Calculator
{
public:virtual int calculate(int x1, int x2){return x1 + x2;}
};class SubCalculate:public Calculator
{
public:virtual int calculate(int x1, int x2){return x1 - x2;}
};int main()
{// 錯誤,抽象類無法進(jìn)行初始化// Calculator* cal = new Calculator();Calculator* Add_Cal = new AddCalculator;cout << Add_Cal->calculate(10, 20) << endl;Calculator* Sub_Cal = new SubCalculate;cout << Sub_Cal->calculate(10, 20) << endl;
}
8.3 虛析構(gòu)/純虛析構(gòu)
多態(tài)使用時,如果子類中有屬性開辟到堆區(qū),那么父類指針在釋放時無法調(diào)用到子類的析構(gòu)代碼。
此時需要在父類中設(shè)置虛析構(gòu)。
虛析構(gòu)和純虛析構(gòu)共性:
- 可以解決父類指針釋放子類對象
- 都需要有具體的函數(shù)實(shí)現(xiàn)
虛析構(gòu)和純虛析構(gòu)區(qū)別:
- 如果是純虛析構(gòu),該類屬于抽象類,無法實(shí)例化對象
#include<iostream>
using namespace std;
// 運(yùn)算器基類
class calculate
{
public:calculate() { cout << "父類的構(gòu)造函數(shù)" << endl; }~calculate() { cout << "父類的析構(gòu)函數(shù)" << endl; }virtual int getresult(int x1, int x2){return 0;}
};// 加法運(yùn)算器
class addcalculate :public calculate
{
public:addcalculate() { cout << "子類的構(gòu)造函數(shù)" << endl; }~addcalculate() { cout << "子類的析構(gòu)函數(shù)" << endl; }int getresult(int x1, int x2){return x1 + x2;}
};int main()
{calculate* calculator = new addcalculate;delete calculator;
}
此時的輸出為:
父類的構(gòu)造
子類的構(gòu)造
父類的析構(gòu)
此時沒有調(diào)用子類的析構(gòu),如果子類在構(gòu)造中開辟了內(nèi)存,在析構(gòu)中釋放了內(nèi)存。此時子類中開辟的內(nèi)存無法被釋放,從而產(chǎn)生內(nèi)存的泄露。
為解決此問題,將父類的析構(gòu)函數(shù)改為虛析構(gòu)函數(shù)。
#include<iostream>
using namespace std;
// 運(yùn)算器基類
class calculate
{
public:calculate() { cout << "父類的構(gòu)造函數(shù)" << endl; }virtual ~calculate() { cout << "父類的析構(gòu)函數(shù)" << endl; }int getresult(int x1, int x2){return 0;}virtual int* test() { return NULL; }
};// 加法運(yùn)算器
class addcalculate :public calculate
{
public:addcalculate() { cout << "子類的構(gòu)造函數(shù)" << endl; val = new int(10);}~addcalculate() { cout << "子類的析構(gòu)函數(shù)" << endl;delete val;}int getresult(int x1, int x2){return x1 + x2;}int* test(){return val;}int* val;
};int main()
{calculate* calculator = new addcalculate();int* space = calculator->test();delete calculator;cout << *space << endl;
}
此時*space為一個垃圾數(shù)據(jù),說明該內(nèi)存內(nèi)部的數(shù)據(jù)已經(jīng)被釋放。如果父類不是虛析構(gòu)函數(shù)的話,輸出就會是10,說明內(nèi)存中的數(shù)據(jù)沒有被釋放。
總結(jié)
- 上一篇: 2018微信网名经典
- 下一篇: C++/C文件读取