運(yùn)算符重載函數(shù)
運(yùn)算符重載,就是對(duì)已有的運(yùn)算符重新進(jìn)行定義,賦予其另一種功能,簡(jiǎn)化操作 讓已有的運(yùn)算符 適應(yīng)不同的數(shù)據(jù)類型 。
- 格式:
重載+=號(hào)運(yùn)算 ==>operator+= 重載+運(yùn)算符 ==>operator+ ...
下面舉兩個(gè)運(yùn)算符重載例子:
- 1.重載+號(hào)
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; } //以全局函數(shù)的形式重載 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; }; 運(yùn)行結(jié)果: real:1 imag:2 real:2 imag:3 complex copy complex copy 3
- 2.重載+=號(hào)運(yùn)算 代碼如下:
class Complex { public: ... //成員函數(shù)重載 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; }; 運(yùn)行結(jié)果: real:1 imag:2 real:2 imag:3 complex copy 3
運(yùn)算符重載的限制
多數(shù)C++運(yùn)算符都可以重載,重載的運(yùn)算符不必是成員函數(shù),但必須至少有一個(gè)操作數(shù)是用戶定義的類型。
-
- 重載后的運(yùn)算符必須至少有一個(gè)操作數(shù)是用戶定義的類型 ,防止用戶為標(biāo)準(zhǔn)類型重載運(yùn)算符。如:不能將減法運(yùn)算符(-)重載為計(jì)算兩個(gè) double 值的和,而不是它們的差。雖然這種限制將對(duì)創(chuàng)造性有所影響,但可以確保程序正常運(yùn)行。
-
- 使用運(yùn)算符時(shí)不能違反運(yùn)算符原來的句法規(guī)則 。例如,不能將求模運(yùn)算符(%)重載成使用一個(gè)操作數(shù):int x;Time shiva;%x;%shiva;,且不能修改運(yùn)算符的優(yōu)先級(jí)。
-
- 不能創(chuàng)建新運(yùn)算符 。例如,不能定義operator **()函數(shù)來表示求冪。
-
4.不能重載下面的運(yùn)算符。
運(yùn)算符重載涉及的知識(shí)點(diǎn)還是比較多的,后期文章會(huì)單獨(dú)出一期講解。
多態(tài)
多態(tài)是指:函數(shù)調(diào)用的多種形態(tài),使用多態(tài)可以使得不同的對(duì)象去完成同一件事時(shí),產(chǎn)生不同的動(dòng)作和結(jié)果
C++中多態(tài)分為 靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài) :
靜態(tài)多態(tài)
靜態(tài)多態(tài)的核心思想
:對(duì)于相關(guān)的對(duì)象類型,直接實(shí)現(xiàn)他們各自的定義,不需要共有基類,甚至可以沒任何關(guān)系, 只需要各個(gè)具體類的實(shí)現(xiàn)中要求相同的接口聲明,這里的接口稱之為隱式接口。客戶端把操作這些對(duì)象的函數(shù)定義為模板,當(dāng)需要操作什么類型的對(duì)象時(shí),直接對(duì)模板指定該類型實(shí)參即可(或通過實(shí)參演繹獲得) 。
在模板編程及泛型編程中,是以隱式接口和編譯器多態(tài)來實(shí)現(xiàn)靜態(tài)多態(tài)。
代碼如下:
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);
};
打印結(jié)果:
Circle draw
Rectangle draw
靜態(tài)多態(tài)本質(zhì)上就是模板的具現(xiàn)化, 靜態(tài)多態(tài)中的接口調(diào)用也叫做隱式接口 ,相對(duì)于顯示接口由函數(shù)的簽名式(也就是函數(shù)名稱、參數(shù)類型、返回類型)構(gòu)成,隱式接口通常由有效表達(dá)式組成
動(dòng)態(tài)多態(tài)
動(dòng)態(tài)多態(tài)核心思想
:對(duì)于相關(guān)的對(duì)象類型,確定它們之間的一個(gè)共同功能集,然后在基類中, 把這些共同的功能聲明為多個(gè)公共的虛函數(shù)接口。各個(gè)子類重寫這些虛函數(shù), 以完成具體的功能??蛻舳说拇a(操作函數(shù))通過指向基類的引用或指針來操作這些對(duì)象, 對(duì)虛函數(shù)的調(diào)用會(huì)自動(dòng)綁定到實(shí)際提供的子類對(duì)象上去。
動(dòng)態(tài)多態(tài)是在運(yùn)行期完成的,這造就了動(dòng)態(tài)多態(tài)機(jī)制在處理異質(zhì)對(duì)象集合時(shí)的強(qiáng)大威力(當(dāng)然,也有了一點(diǎn)點(diǎn)性能損失)。
如下代碼:
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();
};
打印結(jié)果:
Circle draw
Rectangle draw
//動(dòng)態(tài)多態(tài)最吸引人之處在于處理異質(zhì)對(duì)象集合的能力
void DrawVec(std::vector
{
const size_t size = vecGeo.size();
for(size_t i = 0; i < size; ++i)
vecGeo[i]->Draw();
}
}
動(dòng)態(tài)多態(tài)本質(zhì)上就是面向?qū)ο笤O(shè)計(jì)中的繼承、多態(tài)的概念。動(dòng)態(tài)多態(tài)中的接口是顯式接口(虛函數(shù))
動(dòng)態(tài)多態(tài)構(gòu)成條件 :
- 1.必須通過基類的指針或者引用調(diào)用虛函數(shù)。
- 2.被調(diào)用的函數(shù)必須是虛函數(shù),且子類必須對(duì)父類的虛函數(shù)進(jìn)行重寫。
動(dòng)態(tài)多態(tài)實(shí)現(xiàn)原理:虛函數(shù)表
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對(duì)象中除了z成員變量外,實(shí)際上還有一個(gè)指針_vfptr放在對(duì)象的前面(有些平臺(tái)可能會(huì)放到對(duì)象的最后面,這個(gè)跟平臺(tái)有關(guān)).
對(duì)象中的這個(gè)指針叫做虛函數(shù)表指針,簡(jiǎn)稱虛表指針,虛表指針指向一個(gè)虛函數(shù)表,簡(jiǎn)稱虛表,每一個(gè)含有虛函數(shù)的類中都至少有一個(gè)虛表指針。
#include
using namespace std;
//父類
class Base
{
public:
//虛函數(shù)
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
//虛函數(shù)
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
//普通成員函數(shù)
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 1;
};
//子類
class Derive : public Base
{
public:
//重寫虛函數(shù)Func1
virtual void Func1()
{
cout << "Derive::Func1()" << endl;
}
private:
int _d = 2;
};
int main()
{
Base b;
Derive d;
return 0;
}
虛表當(dāng)中存儲(chǔ)的就是虛函數(shù)的地址,因?yàn)楦割惍?dāng)中的Func1和Func2都是虛函數(shù),所以父類對(duì)象b的虛表當(dāng)中存儲(chǔ)的就是虛函數(shù)Func1和Func2的地址。而子類雖然繼承了父類的虛函數(shù)Func1和Func2,但是子類對(duì)父類的虛函數(shù)Func1進(jìn)行了重寫,因此,子類對(duì)象d的虛表當(dāng)中存儲(chǔ)的是父類的虛函數(shù)Func2的地址和重寫的Func1的地址。
這就是為什么虛函數(shù)的重寫也叫做覆蓋,覆蓋就是指虛表中虛函數(shù)地址的覆蓋,重寫是語法的叫法,覆蓋是原理層的叫法。
虛函數(shù)表本質(zhì)是一個(gè)存虛函數(shù)指針的指針數(shù)組,一般情況下會(huì)在這個(gè)數(shù)組最后放一個(gè)nullptr。
當(dāng)滿足多態(tài)條件以后,父類的指針或引用調(diào)用虛函數(shù)時(shí),不是編譯時(shí)確定的,而是運(yùn)行時(shí)到指向的對(duì)象中的虛表中去找對(duì)應(yīng)的虛函數(shù)調(diào)用,并且引用的底層也是由指針實(shí)現(xiàn),父類在指向子類時(shí)會(huì)發(fā)生切片。 所以指針指向父類的對(duì)象,調(diào)用的就是父類的虛函數(shù),指向的是子類對(duì)象,調(diào)用的就是子類的虛函數(shù)。
動(dòng)態(tài)多態(tài)和靜態(tài)多態(tài)的比較
靜態(tài)多態(tài)優(yōu)點(diǎn):
靜態(tài)多態(tài)是在編譯期完成,因此效率高,編譯器可以進(jìn)行優(yōu)化。 有很強(qiáng)的是適配性和松耦合性。 最重要一點(diǎn) 通過模板編程為C++帶來了泛型設(shè)計(jì)的概念 ,比如強(qiáng)大的STL庫
靜態(tài)多態(tài)缺點(diǎn):
由于是模板來實(shí)現(xiàn)靜態(tài)多態(tài),因此 模板的不足也就是靜多態(tài)的劣勢(shì) ,比如調(diào)試?yán)щy、編譯耗時(shí)、代碼膨脹、編譯器支持的兼容性,不能夠處理異質(zhì)對(duì)象集合。
動(dòng)態(tài)多態(tài)優(yōu)點(diǎn):
OO設(shè)計(jì),對(duì)是客觀世界的直覺認(rèn)識(shí);
實(shí)現(xiàn)與接口分離,可復(fù)用;
處理同一繼承體系下異質(zhì)對(duì)象集合的強(qiáng)大威力 ;
動(dòng)態(tài)多態(tài)缺點(diǎn):
運(yùn)行期綁定,導(dǎo)致一定程度的運(yùn)行時(shí)開銷 ;
編譯器無法對(duì)虛函數(shù)進(jìn)行優(yōu)化;
笨重的類繼承體系,對(duì)接口的修改影響整個(gè)類層次;
不同點(diǎn):
本質(zhì)不同,靜態(tài)多態(tài)在編譯期決定,由模板具現(xiàn)完成,而動(dòng)態(tài)多態(tài)在運(yùn)行期決定,由繼承、虛函數(shù)實(shí)現(xiàn);
動(dòng)態(tài)多態(tài)中接口是顯式的,以函數(shù)簽名為中心,多態(tài)通過虛函數(shù)在運(yùn)行期實(shí)現(xiàn),靜態(tài)多臺(tái)中接口是隱式的,以有效表達(dá)式為中心,多態(tài)通過模板具現(xiàn)在編譯期完成。
相同點(diǎn):
都能夠?qū)崿F(xiàn)多態(tài)性,靜態(tài)多態(tài)/編譯期多態(tài)、動(dòng)態(tài)多態(tài)/運(yùn)行期多態(tài);
都能夠使接口和實(shí)現(xiàn)相分離,一個(gè)是模板定義接口,類型參數(shù)定義實(shí)現(xiàn),一個(gè)是基類虛函數(shù)定義接口,繼承類負(fù)責(zé)實(shí)現(xiàn);
總結(jié)
本篇文章詳解講解了C++中的面向?qū)ο缶幊痰娜筇匦裕?strong>封裝,繼承以及多態(tài) 以及對(duì)象編程中模板編程,虛函數(shù),構(gòu)造函數(shù),析構(gòu)函數(shù),拷貝構(gòu)造,操作符重載等知識(shí) , 知識(shí)點(diǎn)還是比較多的,需要好好消化下。
-
JAVA
+關(guān)注
關(guān)注
20文章
2989瀏覽量
109734 -
C++
+關(guān)注
關(guān)注
22文章
2119瀏覽量
75322 -
面向?qū)ο缶幊?/span>
+關(guān)注
關(guān)注
0文章
22瀏覽量
1959
發(fā)布評(píng)論請(qǐng)先 登錄
Labview 之面向對(duì)象編程。 里面有個(gè)例子 和視頻教程地址
C++面向對(duì)象多線程編程 (pdf電子版)
Visual C++面向對(duì)象與可視化程序設(shè)計(jì)習(xí)題解析與編程實(shí)

面向對(duì)象的程序設(shè)計(jì)(C++)
C#入門教程之面向對(duì)象編程簡(jiǎn)介的詳細(xì)資料概述

C++語言和面向對(duì)象程序設(shè)計(jì)教程
STM32 C++編程系列二:STM32 C++代碼封裝初探

嵌入式C語言面向對(duì)象編程應(yīng)用及優(yōu)勢(shì)

評(píng)論