運算符重載函數
運算符重載,就是對已有的運算符重新進行定義,賦予其另一種功能,簡化操作 讓已有的運算符 適應不同的數據類型 。
- 格式:
重載+=號運算 ==>operator+= 重載+運算符 ==>operator+ ...
下面舉兩個運算符重載例子:
- 1.重載+號
class Complex { public: Complex() { } double real, imag; Complex(double _real, double _imag): real(_real),imag(_imag) { cout << "real:" << real << " imag:" << imag << endl; } void print() { cout << "real:" << real << " imag:" << imag << endl; } Complex(const Complex& c) { real = c.real; imag = c.imag; cout << "complex copy" << endl; } //以全局函數的形式重載 friend Complex operator+(const Complex& c1, const Complex& c2); }; Complex operator+(const Complex& c1, const Complex& c2) { Complex _c; _c.real = c1.real + c2.real; _c.imag = c1.imag + c2.imag; return _c; } Complex func() { Complex c(1.0, 2.0); Complex c1(2.0, 3.0); Complex c2 = c + c1; return c2; } void extendsTest::mainTest() { cout << func().real << endl; }; 運行結果: real:1 imag:2 real:2 imag:3 complex copy complex copy 3
- 2.重載+=號運算 代碼如下:
class Complex { public: ... //成員函數重載 Complex& operator+=(const Complex& c); }; Complex & Complex::operator+=(const Complex& c1) { this->real += c1.real; this->imag += c1.imag; return *this; } Complex func() { Complex c(1.0, 2.0); Complex c1(2.0, 3.0); c += c1; return c; } void extendsTest::mainTest() { cout << func().real << endl; }; 運行結果: real:1 imag:2 real:2 imag:3 complex copy 3
運算符重載的限制
多數C++運算符都可以重載,重載的運算符不必是成員函數,但必須至少有一個操作數是用戶定義的類型。
-
- 重載后的運算符必須至少有一個操作數是用戶定義的類型 ,防止用戶為標準類型重載運算符。如:不能將減法運算符(-)重載為計算兩個 double 值的和,而不是它們的差。雖然這種限制將對創造性有所影響,但可以確保程序正常運行。
-
- 使用運算符時不能違反運算符原來的句法規則 。例如,不能將求模運算符(%)重載成使用一個操作數:int x;Time shiva;%x;%shiva;,且不能修改運算符的優先級。
-
- 不能創建新運算符 。例如,不能定義operator **()函數來表示求冪。
-
4.不能重載下面的運算符。
運算符重載涉及的知識點還是比較多的,后期文章會單獨出一期講解。
多態
多態是指:函數調用的多種形態,使用多態可以使得不同的對象去完成同一件事時,產生不同的動作和結果
C++中多態分為 靜態多態和動態多態 :
靜態多態
靜態多態的核心思想
:對于相關的對象類型,直接實現他們各自的定義,不需要共有基類,甚至可以沒任何關系, 只需要各個具體類的實現中要求相同的接口聲明,這里的接口稱之為隱式接口。客戶端把操作這些對象的函數定義為模板,當需要操作什么類型的對象時,直接對模板指定該類型實參即可(或通過實參演繹獲得) 。
在模板編程及泛型編程中,是以隱式接口和編譯器多態來實現靜態多態。
代碼如下:
class Circle {
public:
void Draw() const{
cout << "Circle draw" << endl;
}
int z;
};
class Rectangle {
public:
void Draw() const{
cout << "Rectangle draw" << endl;
}
};
template<typename T>
void test(const T& t) {
t.Draw();
}
void extendsTest::mainTest()
{
//cout << func().real << endl;
Circle cir;
test(cir);
Rectangle rec;
test(rec);
};
打印結果:
Circle draw
Rectangle draw
靜態多態本質上就是模板的具現化, 靜態多態中的接口調用也叫做隱式接口 ,相對于顯示接口由函數的簽名式(也就是函數名稱、參數類型、返回類型)構成,隱式接口通常由有效表達式組成
動態多態
動態多態核心思想
:對于相關的對象類型,確定它們之間的一個共同功能集,然后在基類中, 把這些共同的功能聲明為多個公共的虛函數接口。各個子類重寫這些虛函數, 以完成具體的功能。客戶端的代碼(操作函數)通過指向基類的引用或指針來操作這些對象, 對虛函數的調用會自動綁定到實際提供的子類對象上去。
動態多態是在運行期完成的,這造就了動態多態機制在處理異質對象集合時的強大威力(當然,也有了一點點性能損失)。
如下代碼:
class Geometry {
public:
virtual void Draw() const=0;
};
class Circle :public Geometry{
public:
void Draw() const{
cout << "Circle draw" << endl;
}
int z;
};
class Rectangle :public Geometry {
public:
void Draw() const{
cout << "Rectangle draw" << endl;
}
};
void extendsTest::mainTest()
{
Circle cir;
const Geometry* e1 = ○
e1->Draw();
Rectangle rec;
const Geometry* e2 = &rec;
e2->Draw();
};
打印結果:
Circle draw
Rectangle draw
//動態多態最吸引人之處在于處理異質對象集合的能力
void DrawVec(std::vector
{
const size_t size = vecGeo.size();
for(size_t i = 0; i < size; ++i)
vecGeo[i]->Draw();
}
}
動態多態本質上就是面向對象設計中的繼承、多態的概念。動態多態中的接口是顯式接口(虛函數)
動態多態構成條件 :
- 1.必須通過基類的指針或者引用調用虛函數。
- 2.被調用的函數必須是虛函數,且子類必須對父類的虛函數進行重寫。
動態多態實現原理:虛函數表
class Geometry {
public:
virtual void Draw() const=0;
};
class Circle :public Geometry{
public:
void Draw() const{
cout << "Circle draw" << endl;
}
int z;
};
class Rectangle :public Geometry {
public:
void Draw() const{
cout << "Rectangle draw" << endl;
}
};
void extendsTest::mainTest()
{
Circle cir;
const Geometry* e1 = ○
e1->Draw();
};
Circle對象中除了z成員變量外,實際上還有一個指針_vfptr放在對象的前面(有些平臺可能會放到對象的最后面,這個跟平臺有關).
對象中的這個指針叫做虛函數表指針,簡稱虛表指針,虛表指針指向一個虛函數表,簡稱虛表,每一個含有虛函數的類中都至少有一個虛表指針。
#include
using namespace std;
//父類
class Base
{
public:
//虛函數
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
//虛函數
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
//普通成員函數
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 1;
};
//子類
class Derive : public Base
{
public:
//重寫虛函數Func1
virtual void Func1()
{
cout << "Derive::Func1()" << endl;
}
private:
int _d = 2;
};
int main()
{
Base b;
Derive d;
return 0;
}
虛表當中存儲的就是虛函數的地址,因為父類當中的Func1和Func2都是虛函數,所以父類對象b的虛表當中存儲的就是虛函數Func1和Func2的地址。而子類雖然繼承了父類的虛函數Func1和Func2,但是子類對父類的虛函數Func1進行了重寫,因此,子類對象d的虛表當中存儲的是父類的虛函數Func2的地址和重寫的Func1的地址。
這就是為什么虛函數的重寫也叫做覆蓋,覆蓋就是指虛表中虛函數地址的覆蓋,重寫是語法的叫法,覆蓋是原理層的叫法。
虛函數表本質是一個存虛函數指針的指針數組,一般情況下會在這個數組最后放一個nullptr。
當滿足多態條件以后,父類的指針或引用調用虛函數時,不是編譯時確定的,而是運行時到指向的對象中的虛表中去找對應的虛函數調用,并且引用的底層也是由指針實現,父類在指向子類時會發生切片。 所以指針指向父類的對象,調用的就是父類的虛函數,指向的是子類對象,調用的就是子類的虛函數。
動態多態和靜態多態的比較
靜態多態優點:
靜態多態是在編譯期完成,因此效率高,編譯器可以進行優化。 有很強的是適配性和松耦合性。 最重要一點 通過模板編程為C++帶來了泛型設計的概念 ,比如強大的STL庫
靜態多態缺點:
由于是模板來實現靜態多態,因此 模板的不足也就是靜多態的劣勢 ,比如調試困難、編譯耗時、代碼膨脹、編譯器支持的兼容性,不能夠處理異質對象集合。
動態多態優點:
OO設計,對是客觀世界的直覺認識;
實現與接口分離,可復用;
處理同一繼承體系下異質對象集合的強大威力 ;
動態多態缺點:
運行期綁定,導致一定程度的運行時開銷 ;
編譯器無法對虛函數進行優化;
笨重的類繼承體系,對接口的修改影響整個類層次;
不同點:
本質不同,靜態多態在編譯期決定,由模板具現完成,而動態多態在運行期決定,由繼承、虛函數實現;
動態多態中接口是顯式的,以函數簽名為中心,多態通過虛函數在運行期實現,靜態多臺中接口是隱式的,以有效表達式為中心,多態通過模板具現在編譯期完成。
相同點:
都能夠實現多態性,靜態多態/編譯期多態、動態多態/運行期多態;
都能夠使接口和實現相分離,一個是模板定義接口,類型參數定義實現,一個是基類虛函數定義接口,繼承類負責實現;
總結
本篇文章詳解講解了C++中的面向對象編程的三大特性:封裝,繼承以及多態 以及對象編程中模板編程,虛函數,構造函數,析構函數,拷貝構造,操作符重載等知識 , 知識點還是比較多的,需要好好消化下。
-
JAVA
+關注
關注
19文章
2957瀏覽量
104544 -
C++
+關注
關注
22文章
2104瀏覽量
73487 -
面向對象編程
+關注
關注
0文章
22瀏覽量
1807
發布評論請先 登錄
相關推薦
評論