C++編程面向?qū)ο笕腴T全面詳解
類
1. struct和class的區(qū)別
如果從C語(yǔ)言的視角來(lái)看,所謂類就是能夠調(diào)用自身成員的結(jié)構(gòu)體。而在C++中,關(guān)鍵字struct雖然仍舊保留,但已非C語(yǔ)言中的結(jié)構(gòu)體,而是表示默認(rèn)成員共有的class。
即在C++中,struct C{/*code*/}和class C{public:/**/}并無(wú)區(qū)別,例如下面兩組代碼所實(shí)現(xiàn)的功能是完全一致的。
//默認(rèn)成員公有
struct Number{
private;
float val;
public:
float pubVal;
Number(float inVal);
};
//默認(rèn)成員為私有
class Number{
float val;//外部無(wú)法直接訪問(wèn)
public:
float pubVal;
Number(float inVal);
};
所謂私有成員,就是外部函數(shù)不可訪問(wèn)的成員
void printPublic(Number num){
cout<<num.pubVal<<endl;
}
void printPrivate(Number num){
cout<<num.val<<endl; //報(bào)錯(cuò),無(wú)法訪問(wèn)私有類型
}
不過(guò)從C語(yǔ)言的視角來(lái)看,類也的確保留了一些struct的風(fēng)格,其初始化方法與指針調(diào)用便是明證。
int main(){
Number num{3.14}; //相當(dāng)于引用構(gòu)造函數(shù)
printNumber(num);
Number* pNum = # //指向num的指針
//->表示類指針?biāo)赶虻某蓡T
cout<<pNum->pubVal<<endl;
system("pause");
return 0;
}
輸出為
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 3.14
2. explicit構(gòu)造
由于C++對(duì)泛型具備十分良好的支持,語(yǔ)言本身的強(qiáng)大可能會(huì)導(dǎo)致用戶在使用過(guò)程中不嚴(yán)謹(jǐn),繼而增大維護(hù)成本。例如對(duì)于如下構(gòu)造函數(shù)
Number::Number(float inVal){
val = inVal;
}
那么下面的幾個(gè)語(yǔ)句都能夠輸出正確的值
int main(){
Number num{3.14};
printNumber(num);
num = 1.414;
printNumber(num);
printNumber(0.618);
system("pause");
return 0;
}
結(jié)果為
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 1.414 0.618 請(qǐng)按任意鍵繼續(xù). . .
可見(jiàn)這三條語(yǔ)句都沒(méi)有報(bào)錯(cuò)
Number num{3.14};
num = 1.414;
printNumber(0.618);
第一條是沒(méi)有問(wèn)題的,是簡(jiǎn)單賦值語(yǔ)句;第二條和第三條則是暗中調(diào)用構(gòu)造函數(shù),將浮點(diǎn)類型的變量轉(zhuǎn)換成了Number類型,這種意義不明的代碼自然會(huì)引起維護(hù)上的困難。explicit就為解決這種問(wèn)題而生的。
將構(gòu)造函數(shù)用explicit進(jìn)行標(biāo)記,可以有效禁止這種隱式轉(zhuǎn)換
class Number{
float val;
public:
explicit Number(float inVal);
float pubVal;
};
int main(){
Number num{3.14};
num = 1.414; //編譯不予通過(guò)
printNumber(0.618);//編譯不予通過(guò)
//...
}
3. const和mutable
顧名思義,二者分別是常量與變量,前者要求成員函數(shù)不得修改類的成員變量
class Number{
float val;
public:
mutable float pubVal; //注意該變量用了mutable
explicit Number(float inVal);
void printVal() const; //該方法用了const
};
void Number::printVal() const{
cout<<val<<endl;
/*
val = val+1; //這是不被允許的
*/
pubVal = val+1; //這是被允許的
}
即,const成員只能修改mutable成員。
4. 自引用
自引用是一種編程技巧,對(duì)于更改類狀態(tài)的函數(shù),如果將類本身作為返回值,那么就可以實(shí)現(xiàn)炫酷而優(yōu)雅的鏈?zhǔn)讲僮鳌?/p>
class Number{
float val;
public:
explicit Number(float inVal);
Number& addOne(); //其返回值是當(dāng)前對(duì)象的地址
};
Number& Number::addOne(){
cout<<val++<<endl;
return *this;
}
其中,*this指向調(diào)用該成員函數(shù)的對(duì)象,測(cè)試一下
int main(){
Number num{3.14}; //相當(dāng)于引用構(gòu)造函數(shù)
num.addOne().addOne().addOne();
system("pause");
return 0;
}
結(jié)果為
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 4.14 5.14 請(qǐng)按任意鍵繼續(xù). . .
5. static
顧名思義,靜態(tài)成員之所以被稱為靜態(tài),在于其存儲(chǔ)位置只有一個(gè)。對(duì)于一個(gè)類而言,無(wú)論創(chuàng)建了多少實(shí)例,類中的靜態(tài)變量就只被存儲(chǔ)在那一個(gè)位置。這意味著靜態(tài)成員要比對(duì)象實(shí)例具有更長(zhǎng)的生命周期,當(dāng)一個(gè)對(duì)象被銷毀之后,靜態(tài)成員并沒(méi)有被銷毀,從而再次被調(diào)用的時(shí)候,也不必另行分配內(nèi)存。
class Number{
float val;
static Number defaultNum;
public:
explicit Number(float inVal=0);
static void setDefault(float inVal);
void printVal() const;
};
void Number::printVal() const{
cout<<val<<endl;
}
//定義默認(rèn)Num
Number Number::defaultNum{3.14};
void Number::setDefault(float val){
defaultNum = Number{val};
};
Number::Number(float inVal){
val = inVal ? inVal : defaultNum.val;
}
int main(){
Number num{}; //相當(dāng)于引用構(gòu)造函數(shù)
num.printVal();
system("pause");
return 0;
}
輸出為
PS E:\Code\cpp> .\a.exe 3.14 請(qǐng)按任意鍵繼續(xù). . .
復(fù)數(shù)的實(shí)現(xiàn)
復(fù)數(shù)有實(shí)部和虛部,默認(rèn)值為0,其加法和減法分別就是實(shí)部和虛部相減,其乘法為

#include<iostream>
using namespace std;
class Complex{
float real; //實(shí)部
float im; //虛部
static Complex defaultNum;
public:
explicit Complex(float inReal=0, float inIm=0);
static void setDefault(float inReal, float inIm);
void printVal() const;
Complex& add(float inReal, float inIm);
Complex& minus(float inReal, float inIm);
Complex& multi(float inReal, float inIm);
Complex& div(float inReal, float inIm);
};
//默認(rèn)值為{0,0}
Complex Complex::defaultNum{0,0};
void Complex::setDefault(float inReal,float inIm){
defaultNum = Complex{inReal, inIm};
};
//打印當(dāng)前值
void Complex::printVal() const{
cout<<"real part: "<<real<<endl;
cout<<"image part:"<<im<<endl;
}
//加法
Complex::Complex(float inReal, float inIm){
real = inReal ? inReal : defaultNum.real;
im = inIm ? inIm : defaultNum.im;
}
Complex& Complex::add(float inReal, float inIm){
real += inReal ? inReal : 0;
im += inIm ? inIm : 0;
return *this;
}
Complex& Complex::minus(float inReal, float inIm){
real -= inReal ? inReal : 0;
im -= inIm ? inIm : 0;
return *this;
}
Complex& Complex::multi(float inReal, float inIm){
float temp = real*inReal - im*inIm;
im = real*inIm + im*inReal;
real = temp;
return *this;
}
Complex& Complex::div(float inReal, float inIm){
float temp = inReal*inReal + inIm*inIm;
float tempReal = (real*inReal + im*inIm)/temp;
im = (im*inReal-real*inIm)/temp;
real = tempReal;
return *this;
}
int main(){
Complex num{}; //相當(dāng)于引用構(gòu)造函數(shù)
num.add(1,2).multi(3,4).div(1,2);
num.printVal();
system("pause");
return 0;
}
下面的操作便基于這個(gè)復(fù)數(shù)類進(jìn)行。
6.成員函數(shù)重載
上述的加減乘除運(yùn)算,默認(rèn)輸入值為實(shí)部和虛部的組合,但并不能實(shí)現(xiàn)兩個(gè)Complex的運(yùn)算。C++支持成員函數(shù)的重載。
class Complex{
/*
上文中所定義的類的結(jié)尾
*/
Complex operator+(Complex);
Complex operator-(Complex);
Complex operator*(Complex);
Complex operator/(Complex);
//實(shí)現(xiàn)類似數(shù)乘功能
Complex operator*(float);
Complex operator/(float);
}
這些函數(shù)可以通過(guò)最簡(jiǎn)單的方式定義
Complex& Complex::add(Complex num){
real += num.real;
im += num.im;
return *this;
}
也可以通過(guò)調(diào)用已經(jīng)定義過(guò)的成員函數(shù)
Complex& Complex::multi(Complex num){
multi(num.real, num.im);
return *this;
}
7.運(yùn)算符重載
在C++中,可以很方便地對(duì)一些運(yùn)算符進(jìn)行重載,其格式為
Complex operator+(Complex);
對(duì)于兩個(gè)復(fù)數(shù)a和b來(lái)說(shuō),調(diào)用重載之后的運(yùn)算符a+b等價(jià)于a.operator(b)。
其具體實(shí)現(xiàn)為
class Complex{
/*
上文中所定義的類的結(jié)尾
*/
Complex operator+(Complex);
Complex operator-(Complex);
Complex operator*(Complex);
Complex operator/(Complex);
}
Complex Complex::operator+(Complex num){
float outReal = real+num.real;
float outIm = im+num.im;
return Complex{outReal, outIm};
}
Complex Complex::operator-(Complex num){
return Complex{real-num.real, im-num.im};
}
Complex Complex::operator*(Complex num){
return Complex{real*num.real - im*num.im,
real*num.im + im*num.real};
}
Complex Complex::operator/(Complex num){
float temp = num.real*num.real + num.im*num.im;
return Complex{(real*num.real + im*num.im)/temp,
(im*num.real-real*num.im)/temp};
}
Complex Complex::operator*(float val){
return Complex{real*val,im*val};
}
Complex Complex::operator/(float val){
return Complex{real/val,im/val};
}
//主函數(shù)
int main(){
Complex temp{1,1};
Complex temp1 = temp-temp*temp*2;
temp1.printVal();
temp.printVal();
system("pause");
return 0;
}
測(cè)試一下結(jié)果為
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe real part: 1 image part:-3 real part: 1 image part:1
可見(jiàn)操作符雖然被重載了,但運(yùn)算次序得以保留。
8.new
C語(yǔ)言中通過(guò)STRUCT* struct = (STRUCT*)malloc(sizeof(STRUCT))的方式來(lái)動(dòng)態(tài)地開(kāi)辟內(nèi)存,留待日后使用。
在C++中,new可以勝任這一工作。
例如
int* p = new int; int* Q = new int(5);
對(duì)于Complex類,可以通過(guò)指針形式進(jìn)行實(shí)現(xiàn)
int main(){
Complex* temp = new Complex(1,1);
temp->add(*temp);
temp->printVal();
delete(temp); //銷毀temp內(nèi)存
system("pause");
return 0;
}
其中,->亦承自C語(yǔ)言,用于類指針調(diào)用類成員,其結(jié)果為
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe real part: 2 image part:2 請(qǐng)按任意鍵繼續(xù). . .
9.析構(gòu)函數(shù)
一般通過(guò)new來(lái)分配內(nèi)存空間,需要在調(diào)用結(jié)束之后使用delete對(duì)內(nèi)存進(jìn)行釋放,delete的執(zhí)行過(guò)程,便會(huì)調(diào)用析構(gòu)函數(shù)。
在解釋析構(gòu)函數(shù)之前,需要回顧一下構(gòu)造函數(shù),所謂構(gòu)造函數(shù),即與類名相同的函數(shù),通過(guò)構(gòu)造函數(shù)可以創(chuàng)建一個(gè)類的對(duì)象,并開(kāi)辟足夠的內(nèi)存。析構(gòu)函數(shù)即銷毀函數(shù),將構(gòu)造函數(shù)開(kāi)辟的內(nèi)存銷毀掉。
析構(gòu)函數(shù)亦與類名相同,而且無(wú)參數(shù)無(wú)return不可重載,是一個(gè)不易理解但易于使用的方法。
public:
explicit Complex(float inReal=0, float inIm=0);
//此即析構(gòu)函數(shù),
~Complex(){}
10.friend
在復(fù)數(shù)類中,實(shí)部和虛部被封裝為私有變量,外部函數(shù)是無(wú)法訪問(wèn)的。此時(shí),如果希望在其他類中創(chuàng)建一個(gè)提取復(fù)數(shù)實(shí)部或虛部的變量,則可以考慮友元機(jī)制。
所謂友元機(jī)制,即允許一個(gè)類將其非共有成員授權(quán)給指定的函數(shù)或者類,通過(guò)關(guān)鍵字friend修飾。例如,
/*
Complex類
*/
friend float getReal(Complex num);
};
float getReal(Complex num){
cout<<num.real<<endl;
return num.real;
}
這樣,getReal就可以直接訪問(wèn)Complex類的私有成員。
11.類的繼承
一般來(lái)說(shuō),復(fù)數(shù) a + b i a+b\text{i} a+bi并不支持類似直乘的操作,即 ( a + b i ) ∗ ( a + b i ) ≠ a b + c d i (a+b\text{i})*(a+b\text{i})\not ={ab+cd\text{i}} (a+bi)∗(a+bi)=ab+cdi,那么如果希望構(gòu)造一種新的代數(shù)關(guān)系,使之既支持復(fù)數(shù)乘法,又可以直乘,那么就需要新建一個(gè)類,為了避免代碼過(guò)于重復(fù),這個(gè)類可以作為復(fù)數(shù)類的派生類而存在。
需要注意的一點(diǎn)是,此前所創(chuàng)建的Complex類默認(rèn)成員為私有,所以其im,real對(duì)于子類而言是不可訪問(wèn)的。出于簡(jiǎn)單考慮,我們將class改為struct,這樣其子類便可無(wú)痛調(diào)用。
//Complex類的派生類
class antiComplex : Complex{
public:
antiComplex(float inReal,float inIm){
real = inReal;
im = inIm;
};
void printVal();
antiComplex operator*(antiComplex);
};
antiComplex antiComplex::operator*(antiComplex num){
return antiComplex{real*num.real,im*num.im};
}
//重寫(xiě)printVal函數(shù)
void antiComplex::printVal(){
cout<<"I'm antiComplex"<<endl;
cout<<"real part: "<<real<<endl
<<"image part:"<<im<<endl;
}
int main(){
antiComplex temp{1,2};
temp.printVal();
temp = temp*temp;
temp.printVal();
system("pause");
return 0;
}
其結(jié)果為
PS E:\Code\cpp> .\a.exe I'm antiComplex real part: 1 image part:2 I'm antiComplex real part: 1 image part:4 請(qǐng)按任意鍵繼續(xù). . .
在C++中有三種繼承方式,分別是public,private,protected,一般默認(rèn)為public繼承,其特點(diǎn)是無(wú)法訪問(wèn)父類的私有成員;private繼承則連公有成員和保護(hù)成員都無(wú)法訪問(wèn);protected則允許其子類訪問(wèn),但不允許子類的子類訪問(wèn)。
具體表現(xiàn)如下表所示,其讀法為public成員在private繼承時(shí)表現(xiàn)為private成員。
| public繼承 | private繼承 | protected繼承 | |
|---|---|---|---|
| public成員 | public | private | protected |
| private成員 | private | private | private |
| protected成員 | protected | private | protected |
12.多態(tài)
所謂多態(tài)就是多個(gè)子類繼承一個(gè)基類時(shí)的差異性,例如,Complex和antiComplex都可以作為一種抽象數(shù)據(jù)結(jié)構(gòu)的子類,畢竟二者只有在乘除法上表現(xiàn)不同。
#include<iostream>
using namespace std;
struct Abstract{
float real;
float im;
Abstract(float inReal, float inIm){
real = inReal;
im = inIm;
}
void printVal(){
cout<<"I'm Abstract"<<endl;
};
Abstract& multi(Abstract val){};
};
struct Complex:Abstract{
Complex(float inReal, float inIm)
:Abstract(inReal,inIm){}
void printVal();
Abstract& multi(Abstract val);
};
void Complex::printVal(){
cout<<"I'm Complex:"
<<real<<"+"<<im<<"i"<<endl;
}
Abstract& Complex::multi(Abstract val){
float temp = real*val.real - im*val.im;
im = real*val.real + im*val.im;
real = temp;
return *this;
}
struct antiComplex:Abstract{
antiComplex(float inReal, float inIm)
:Abstract(inReal,inIm){}
void printVal();
Abstract& multi(Abstract val);
};
void antiComplex::printVal(){
cout<<"I'm antiComplex:"
<<real<<"+"<<im<<"j"<<endl;
}
Abstract& antiComplex::multi(Abstract val){
real = real*val.real;
im = im*val.im;
return *this;
}
int main(){
Complex temp{1,2};
antiComplex antemp{1,2};
temp.multi(temp).multi(temp);
antemp.multi(antemp).multi(temp);
temp.printVal();
antemp.printVal();
system("pause");
return 0;
}
其輸出結(jié)果為
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe I'm Complex:-3+5i I'm antiComplex:1+4j 請(qǐng)按任意鍵繼續(xù). . .
可見(jiàn)這個(gè)結(jié)果是錯(cuò)的,原因在于multi函數(shù)的返回指針為Abstract類型,而基類中的multi為一個(gè)空函數(shù),所以只執(zhí)行一次multi。所以,如果子類再調(diào)用函數(shù)之后繼續(xù)保持子類的方法就好了,這就需要使用關(guān)鍵字virtual。
13.virtual
直接通過(guò)指針來(lái)說(shuō)明virtual的功能比較合適。
struct Abstract{
float real;
float im;
Abstract(float inReal, float inIm){
real = inReal;
im = inIm;
}
void printVal(){
cout<<"I'm Abstract"<<endl;
};
};
struct Complex:Abstract{
Complex(float inReal, float inIm)
:Abstract(inReal,inIm){}
void printVal(){
cout<<"I'm Complex:"
<<real<<"+"<<im<<"i"<<endl;
}
};
int main(){
Abstract* a;
Complex temp{1,2};
a = &temp;
a->printVal();
system("pause");
return 0;
}
其運(yùn)行結(jié)果為
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe I'm Abstract
也就是說(shuō),雖然父類的指針指向了子類的對(duì)象,但最終指針指向的仍然是父類的函數(shù)。而如果在父類函數(shù)前加上virtual關(guān)鍵字,即將其改為
struct Abstract{
float real;
float im;
Abstract(float inReal, float inIm){
real = inReal;
im = inIm;
}
virtual void printVal(){
cout<<"I'm Abstract"<<endl;
};
};
則其輸出為
PS E:\Code\cpp> .\a.exe I'm Complex:1+2i
可見(jiàn)虛函數(shù)的作用是使得父類指針指向?qū)嶋H對(duì)象的方法。將父類的multi函數(shù)變?yōu)?/p>
virtual Abstract& multi(Abstract val){};
則最終的輸出結(jié)果為
PS E:\Code\cpp> .\a.exe I'm Complex:-16+34i I'm antiComplex:-16+136j
看的不過(guò)癮?
以上就是C++面向?qū)ο笕腴T全面詳解的詳細(xì)內(nèi)容,更多關(guān)于C++面向?qū)ο笕腴T的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
6個(gè)變態(tài)的C語(yǔ)言Hello World程序
這篇文章主要介紹了6個(gè)變態(tài)的C語(yǔ)言Hello World程序,需要的朋友可以參考下2016-05-05
C++友元函數(shù)與拷貝構(gòu)造函數(shù)詳解
這篇文章主要介紹了C++友元函數(shù)與拷貝構(gòu)造函數(shù),需要的朋友可以參考下2014-07-07
VC使用TerminateProcess結(jié)束進(jìn)程實(shí)例
這篇文章主要介紹了VC使用TerminateProcess結(jié)束進(jìn)程的方法,實(shí)例演示了TerminateProcess結(jié)束進(jìn)程的具體實(shí)現(xiàn)過(guò)程,在進(jìn)行VC應(yīng)用程序開(kāi)發(fā)時(shí)非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
c++中的字節(jié)序與符號(hào)位的問(wèn)題
這篇文章主要介紹了c++中的字節(jié)序與符號(hào)位的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
OpenMP?Parallel?Construct的實(shí)現(xiàn)原理詳解
在本篇文章當(dāng)中我們將主要分析?OpenMP?當(dāng)中的?parallel?construct?具體時(shí)如何實(shí)現(xiàn)的,以及這個(gè)?construct?調(diào)用了哪些運(yùn)行時(shí)庫(kù)函數(shù),并且詳細(xì)分析這期間的參數(shù)傳遞,需要的可以參考一下2023-01-01
c++中struct和class的區(qū)別小結(jié)
在C++中,class和struct都是用于定義自定義數(shù)據(jù)類型的關(guān)鍵字,本文主要介紹了c++中struct和class的區(qū)別小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
詳解C/C++ Linux出錯(cuò)處理函數(shù)(strerror與perror)的使用
我們知道,系統(tǒng)函數(shù)調(diào)用不能保證每次都成功,必須進(jìn)行出錯(cuò)處理,這樣一方面可以保證程序邏輯正常,另一方面可以迅速得到故障信息。本文主要為大家介紹兩個(gè)出錯(cuò)處理函數(shù)(strerror、perror)的使用,需要的可以參考一下2023-01-01
cin.get()和cin.getline()之間的區(qū)別
以下是對(duì)cin.get()和cin.getline()的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-09-09

