通過operator關鍵字可以定義特殊的函數,operator本質是通過函數重載操作符。
創新互聯專注于晉安企業網站建設,響應式網站設計,成都商城網站開發。晉安網站建設公司,為晉安等地區提供建站服務。全流程按需定制開發,專業設計,全程項目跟蹤,創新互聯專業和態度為您提供的服務
Type operator operatorname(const Type p1, const Type p2)
{
Type ret;
return ret;
}
可以將操作符重載函數聲明為友元函數。
#include <iostream>
using namespace std;
class Complex
{
public:
Complex(float x=0, float y=0)
{
this->x = x;
this->y = y;
}
void print()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
friend const Complex operator+(const Complex &c1,const Complex &c2);
private:
float x;
float y;
};
const Complex operator+(const Complex &c1,const Complex &c2)
{
return Complex(c1.x + c2.x,c1.y + c2.y);
}
int main(int argc, char *argv[])
{
Complex c1(2,3);
Complex c2(3,4);
c1.print();
c2.print();
Complex c3 = c1 + c2;
c3.print();
Complex c4 = operator+(c1,c2);
c4.print();
return 0;
}
上述代碼中,編譯器會檢查是否有可用的操作符重載函數,因此Complex c3 = c1 + c2;代碼也是合法的。
將操作符重載函數定義為類成員函數。
#include <iostream>
using namespace std;
class Complex
{
public:
Complex(float x=0, float y=0)
{
this->x = x;
this->y = y;
}
void print()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
const Complex operator+(const Complex &another)
{
cout << "member function." << endl;
return Complex(this->x + another.x, this->y + another.y);
}
friend const Complex operator+(const Complex &c1,const Complex &c2);
private:
float x;
float y;
};
const Complex operator+(const Complex &c1,const Complex &c2)
{
cout << "friend global function." << endl;
return Complex(c1.x + c2.x,c1.y + c2.y);
}
int main(int argc, char *argv[])
{
Complex c1(2,3);
Complex c2(3,4);
c1.print();
c2.print();
//成員函數
Complex c3 = c1 + c2;
c3.print();
//成員函數
Complex c4 = c1.operator +(c2);
c4.print();
//全局函數
Complex c5 = operator+(c1,c2);
c4.print();
return 0;
}
操作符重載函數作為類的成員函數時,比全局操作符重載函數少一個參數,不需要依賴友元就可以完成操作符重載,編譯器會優先在類的成員函數中查找操作符重載函數。因此Complex c3 = c1 + c2;代碼會優先調用類的操作符重載成員函數。
操作符重載的規則:
A、C++不允許用戶自己定義新的運算符,只能對已有的 C++運算符進行重載。
B、C++語言中大部分運算符都可以重載,成員選擇符(.)、成員對象選擇符(.*)、域解析操作符(::)、條件操作符(?:)、sizeof不可以重載。除了賦值操作符(=)外,基類中重載的操作符都將被派生類繼承。
C、重載不能改變運算符運算對象(即操作數)的個數。
D、重載不能改變運算符的優先級別。
E、重載不能改變運算符的結合性。
F、重載運算符的函數不能有默認的參數
G、重載的運算符必須和用戶定義的自定義類型的對象一起使用,其參數至少應有一個是類對象(或類對象的引用)。參數不能全部是 C++的標準類型,以防止用戶修改用于標準類型數據成員的運算符的性質。
H、用于類對象的運算符一般必須重載,但有兩個例外,運算符”=“和運算符”&“不必用戶重載。
I、應當使重載運算符的功能類似于該運算符作用于標準類型數據時候時所實現的功能。
J、運算符重載函數可以是類的成員函數,也可以是類的友元函數,還可以是既非類的成員函數也不是友元函數的普通函數。
K、賦值操作符只能重載為成員函數
形式:L#R
全局函數:operator#(L,R);
成員函數:L.operator#(R)
operator+=實例:
#include <iostream>
using namespace std;
class Complex
{
public:
Complex(double x = 0, double y = 0)
{
this->x = x;
this->y = y;
}
void print()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
Complex& operator +=(const Complex &c)
{
this->x += c.x;
this->y += c.y;
return * this;
}
private:
double x;
double y;
};
形式:#M 或 M#
全局函數:operator#(M)
成員函數:M.operator#()
operator-實例:
#include <iostream>
using namespace std;
class Complex
{
public:
Complex(double x = 0, double y = 0)
{
this->x = x;
this->y = y;
}
void print()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
const Complex operator-(void) const
{
return Complex(-x,-y);
}
private:
double x;
double y;
};
函數形式
istream & operator>>(istream &,自定義類&);
ostream & operator<<(ostream &,自定義類&);
流輸入輸出運算符重載通過友元來實現,避免修改C++的標準庫。
operator<< 和operator>>實例:
class Complex
{
public:
Complex(double x = 0, double y = 0)
{
this->x = x;
this->y = y;
}
void print()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
friend ostream & operator<<(ostream &os, const Complex & c);
friend istream & operator>>(istream &is, Complex &c);
private:
double x;
double y;
};
ostream & operator<<(ostream &os, const Complex & c)
{
os<<"("<<c.x<<","<<c.y<<")";
return os;
}
istream & operator>>(istream &is, Complex &c)
{
is>>c.x>>c.y;
return is;
}
. (成員訪問運算符)
.* (成員指針訪問運算符)
:: (域運算符)
sizeof (長度運算符)
?: (條件運算符)
= 賦值運算符
[] 下標運算符
() 函數運算符
-> 間接成員訪問
編譯器默認重載了賦值運算符,但編譯器默認重載的賦值操作符僅完成淺拷貝,需要深拷貝操作必須自定義重載賦值操作符。
A、一個操作符的左右操作數不一定是相同類型的對象,這就涉及到將該操作符函數定義為誰的友元,誰的成員問題。
B、一個操作符函數,被聲明為哪個類的成員,取決于該函數的調用對象(通常是左操作數)。
C、一個操作符函數,被聲明為哪個類的友員,取決于該函數的參數對象(通常是右操作數)。
#include <iostream>
using namespace std;
class Mail;
class Sender
{
public:
Sender(string s):_addr(s){}
Sender& operator<<(const Mail & mail); //成員
private:
string _addr;
};
class Mail
{
public:
Mail(string _t,string _c ):_title(_t),_content(_c){}
friend Sender& Sender::operator<<(const Mail & mail);
//友元
private:
string _title;
string _content;
};
Sender& Sender::operator<<(const Mail & mail)
{
cout<<"Address:"<<mail._addr<<endl;
cout<<"Title :"<<mail._title<<endl;
cout<<"Content:"<<mail._content<<endl;
return *this;
}
int main()
{
Sender sender("guilin_wang@163.com");
Mail mail("note","meeting at 3:00 pm");
Mail mail2("tour","One night in beijing");
sender<<mail<<mail2;
return 0;
}
A、邏輯運算操作符:
運算符重載本質是函數重載,C++對邏輯操作符重載時將邏輯操作符定義為函數,但是由于函數參數的計算次序是不確定的,導致邏輯操作符原生的短路法則將失效,因此不推薦對邏輯操作符進行重載。工程實踐中需要使用重載比較操作符等方法避免重載邏輯操作符的陷阱,直接使用成員函數代替邏輯操作符重載,使用全局函數對邏輯操作符進行重載。
#include <iostream>
using namespace std;
class Test
{
public:
Test(int ok = true)
{
this->ok = ok;
}
int value()const
{
cout << "call value(),ok = " << ok << endl;
return ok;
}
Test operator +(const Test& another)
{
this->ok += another.ok;
return *this;
}
private:
int ok;
};
//&&操作符重載函數
bool operator &&(const Test& left, const Test& right)
{
return left.value() && right.value();
}
//||操作符重載函數
bool operator ||(const Test& left, const Test& right)
{
return left.value() || right.value();
}
Test func(Test test)
{
cout << "Test func(Test test): i = "<< test.value() <<endl;
return test;
}
int main(int argc, char *argv[])
{
Test test0(0);
Test test1(1);
Test test2(2);
Test test3(3);
if(test0 && test1)
{
cout << "result is true" << endl;
}
else
{
cout << "result is false" << endl;
}
/*****************************
*call value(),ok = 0
*result is false
* 上述測試代碼:短路法則正常
* **************************/
cout << endl;
if(operator &&(func(test0), func(test1)))
{
cout << "result is true" << endl;
}
else
{
cout << "result is false" << endl;
}
cout << endl;
/*****************************
*call value(),ok = 1
*Test func(Test test): i = 1
*call value(),ok = 0
*Test func(Test test): i = 0
*call value(),ok = 0
*result is false
*上述測試代碼:短路法則失效
* **************************/
if((test2 + test3) && test0)
{
cout << "result is true" << endl;
}
else
{
cout << "result is false" << endl;
}
cout << endl;
/*****************************
*call value(),ok = 5
*call value(),ok = 0
*result is false
* **************************/
if(test0 || test1)
{
cout << "result is true" << endl;
}
else
{
cout << "result is false" << endl;
}
cout << endl;
/*****************************
*call value(),ok = 0
*call value(),ok = 1
*result is true
* **************************/
if(operator &&(test0, test1))
{
cout << "result is true" << endl;
}
else
{
cout << "result is false" << endl;
}
cout << endl;
/*****************************
*call value(),ok = 0
*result is false
*上述測試代碼:短路法則正常
* **************************/
if(operator ||(test0 + test1, test3))
{
cout << "result is true" << endl;
}
else
{
cout << "result is false" << endl;
}
/*****************************
*call value(),ok = 1
*result is true
*上述測試代碼:短路法則正常
* **************************/
return 0;
}
上述測試代碼中,如果邏輯操作符的操作數中是需要計算的函數調用,短路法則可能會失效。
B、逗號操作符:
可以使用全局函數對逗號操作符進行重載,重載函數的參數必須有一個是類類型,返回值類型必須是引用。
class& operator,(const class& a, const class& b)
{
return const_cast<class&>(b);
}
重載逗號操作符后,逗號表達式無法嚴格從左向右計算表達式,不能重載逗號操作符,重載后的逗號操作符沒有了原生的語義。
原因:操作符的重載本質是函數調用,函數調用在進入函數體前需要完成所有參數的計算,參數的計算次序是不確定的,因此重載逗號操作符后無法保證逗號操作符的原生語義。
逗號操作符不需要重載,重載的逗號操作符無法嚴格從左向右計算逗號表達式,失去了原生逗號操作符的語義。
C、前置操作符與后置操作符
前置操作符和后置操作符支持全局函數、類成員函數重載。
前置的++、--運算操作符可以重載,不需要額外的參數。
后置的++、--運算操作符可以重載,需要一個int類型的占位參數。
#include <iostream>
using namespace std;
class Test
{
public:
Test(int i = 0)
{
this->i = i;
}
//前置操作符++
Test& operator ++()
{
++i;
return *this;
}
//前置操作符--
Test& operator --()
{
--i;
return *this;
}
//后置操作符--
Test operator ++(int)
{
Test ret(i);
i++;
return ret;
}
//后置操作符--
Test operator --(int)
{
Test ret(i);
i--;
return ret;
}
int value()const
{
return i;
}
private:
int i;
};
int main(int argc, char *argv[])
{
Test test1(1);
cout << (++test1).value() << endl;
Test test2(1);
cout << (--test2).value() << endl;
Test test3(1);
cout << (test3++).value() << endl;
Test test4(1);
cout << (test4--).value() << endl;
return 0;
}
由于類的前置操作符重載函數內部沒有額外的臨時對象開銷,類的前置操作符重載函數效率比后置操作符高。
int i = 0;
i++;
++i;
對于C++基礎類型,前置操作符和后置操作符效率基本相同。
現代C++編譯器會對編譯代碼進行優化,使得編譯后的二進制代碼更加高效,優化后的二進制代碼可能失去C/C++代碼的原生語義。
C++語言中,標準類型之間的轉換一般有隱式和顯示轉換,用戶自定義類型間的轉換則需要自定義專門的轉換函數。
C語言中,基本類型間會進行隱式的類型安全轉換,轉換規則如下:
int a = -2000;
unsigned int b = 1000;
cout << a + b << endl;//4294966296
上述代碼中,int與unsigned int運算時int會被轉換為unsigned int,此時a會被轉換為unsigned int,是一個非常大的數。
short s = 12;
char c = '1';
cout << sizeof(s + c) << endl;//4
上述代碼中,C++編譯器會進行優化,編譯器遇到short與char進行運算時會將short和char都轉換為int,便于高效計算。
C++語言兼容了C語言的隱式類型安全轉換。
基本類型間的轉換如下:
A、隱式轉換5.0/8
B、顯示轉換(float)5/8
C++的類類型之間的轉換的規則如下:
A、轉換函數定義在源對象類(待轉換對象中)中,是轉換源的成員函數。
B、一旦為轉換源類型提供了到目標類型的轉化操作符函數,就可以將源類型對象以隱式轉化的方式得的目標類型的對象。
C、應用于構造及初始化,賦值,傳參,返回等等場合。
#include <iostream>
using namespace std;
class Test
{
public:
Test(int i = 0)
{
this->i = i;
cout << "Test(int i = 0) i = " << i << endl;
}
private:
int i;
};
int main(int argc, char *argv[])
{
Test test;//Test(int i = 0) i = 0
test = 10;//Test(int i = 0) i = 10
return 0;
}
上述代碼中,編譯器會將10使用構造函數Test(int i = 0)隱式轉換為Test對象。
實際工程中類的隱式類型轉換是不安全的,編譯器會盡力去查找構造函數轉化不同的類型,如果沒有匹配的構造函數才會報錯,因此不可以使用隱式類型轉換,需要使用explicit關鍵字聲明編譯器不能隱式類型轉換,而需要顯示的聲明類型轉換。
顯示聲明類對象類型轉換的使用方式:
static_cast<classname>(value);
classname(value);
(calssname)value;//不推薦
#include <iostream>
using namespace std;
class Test
{
public:
explicit Test(int i = 0)
{
this->i = i;
cout << "Test(int i = 0) i = " << i << endl;
}
private:
int i;
};
int main(int argc, char *argv[])
{
Test test;//Test(int i = 0) i = 0
test = static_cast<Test>(10);//Test(int i = 0) i = 10
test = Test(100);//Test(int i = 0) i = 100
return 0;
}
上述代碼中,構造函數使用explicit關鍵字進行聲明,編譯器不能再進行隱式的類型轉換,只能使用顯示的類型轉換。
類型轉換構造函數聲明如下:classname(const anotherclass & another);
#include <iostream>
using namespace std;
class Point3D;
class Point2D
{
public:
Point2D(int x = 0,int y = 0)
{
this->x = x;
this->y = y;
}
void print()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
friend class Point3D;
private:
int x;
int y;
};
class Point3D
{
public:
Point3D(int x = 0, int y = 0, int z = 0)
{
this->x = x;
this->y = y;
this->z = z;
}
//類型轉換構造函數
Point3D(const Point2D &p)
{
this->x = p.x;
this->y = p.y;
this->z = 0;
cout << "Point3D(const Point2D &p)" <<endl;
}
void print()
{
cout<<"("<<x<<","<<y<<","<<z<<")"<<endl;
}
private:
int x;
int y;
int z;
};
int main(int argc, char *argv[])
{
Point2D p2(1,2);
p2.print();
Point3D p3(3,4,5);
p3.print();
Point3D p3a = p2;//Point3D(const Point2D &p)
p3a.print();
return 0;
}
上述代碼中,Point3D類提供了一個類型轉換構造函數,用于將Point2D類對象轉換為Point3D類型。
類型轉換函數可以將類對象轉換為其它類型。
類型轉換函數聲明的語法如下:operator Type(void);
#include <iostream>
using namespace std;
class Point3D;
class Point2D
{
public:
explicit Point2D(int x = 0,int y = 0)
{
this->x = x;
this->y = y;
}
void print()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
private:
int x;
int y;
friend class Point3D;
};
class Point3D
{
public:
explicit Point3D(int x = 0, int y = 0, int z = 0)
{
this->x = x;
this->y = y;
this->z = z;
}
//類型轉換構造函數
Point3D(const Point2D &p)
{
this->x = p.x;
this->y = p.y;
this->z = 0;
cout << "Point3D(const Point2D &p)" <<endl;
}
operator Point2D()
{
cout << "operator Point2D()" << endl;
Point2D p2;
p2.x = x;
p2.y = y;
return p2;
}
void print()
{
cout<<"("<<x<<","<<y<<","<<z<<")"<<endl;
}
private:
int x;
int y;
int z;
};
int main(int argc, char *argv[])
{
Point3D p3(3,4,5);
p3.print();
Point2D p2(1,2);
p2.print();
Point2D p2a = p3;//operator Point2D()
p2a.print();
return 0;
}
上述代碼中,使用Point3D對象對Point2D對象進行初始化時會調用operator Point2D()類型轉換操作符函數。
類型轉換操作符函數可能和類型轉換構造函數沖突,可以使用explicit對類型轉換構造函數聲明避免沖突。工程中實際使用普通的classname toClassName()公有成員函數進行類型轉換。
#include <iostream>
using namespace std;
class Point3D;
class Point2D
{
public:
explicit Point2D(int x = 0,int y = 0)
{
this->x = x;
this->y = y;
}
void print()
{
cout<<"("<<x<<","<<y<<")"<<endl;
}
private:
int x;
int y;
friend class Point3D;
};
class Point3D
{
public:
explicit Point3D(int x = 0, int y = 0, int z = 0)
{
this->x = x;
this->y = y;
this->z = z;
}
//類型轉換構造函數
Point3D(const Point2D &p)
{
this->x = p.x;
this->y = p.y;
this->z = 0;
cout << "Point3D(const Point2D &p)" <<endl;
}
operator Point2D()
{
cout << "operator Point2D()" << endl;
Point2D p2;
p2.x = x;
p2.y = y;
return p2;
}
Point2D toPoint2D()
{
Point2D p2;
p2.x = x;
p2.y = y;
return p2;
}
void print()
{
cout<<"("<<x<<","<<y<<","<<z<<")"<<endl;
}
private:
int x;
int y;
int z;
};
int main(int argc, char *argv[])
{
Point3D p3(3,4,5);
p3.print();
Point2D p2(1,2);
p2.print();
Point2D p2a = p3;//operator Point2D()
p2a.print();
Point2D p2b = p3.toPoint2D();
p2b.print();
return 0;
}
函數對象也成仿函數(functor),可以使用具體的類對象取代類成員函數。函數對象通過重載函數操作符實現,函數操作符只能重載為類的成員函數,可以定義不同參數的多個重載函數。
函數操作符重載函數聲明如下:Type operator()()
函數操作符主要應用于STL和模板。函數操作符只能通過類成員函數重載,函數對象用于在工程中取代函數指針。
#include <iostream>
using namespace std;
class Fib
{
private:
int a0;
int a1;
public:
Fib()
{
a0 = 0;
a1 = 1;
}
Fib(int n)
{
a0 = 0;
a1 = 1;
for(int i = 0; i < n; i++)
{
int t = a1;
a1 = a0 + a1;
a0 = t;
}
}
int operator()()
{
int ret = a1;
a1 = a0 + a1;
a0 = ret;
return ret;
}
};
int main()
{
Fib fib;
for(int i = 0; i < 10; i++)
{
cout << fib() << endl;
}
cout << endl;
Fib fib2(10);//從第10項開始
for(int i = 0; i < 5; i++)
{
cout << fib2() << endl;
}
return 0;
}
定義:
operator new
operator delete
operator new[]
operator delete[]
A、全局函數重載
#include <iostream>
#include <stdlib.h>
using namespace std;
class A
{
public:
A()
{
cout<<"A constructor"<<endl;
}
~A()
{
cout<<"A destructor"<<endl;
}
private:
int a;
};
void * operator new (size_t size)
{
cout<<"new "<<size<<endl;
return malloc(size);
}
void operator delete(void *p)
{
cout<<"delete"<<endl;
free(p);
}
void * operator new[] (size_t size)
{
cout<<"new[] "<<size<<endl;
return malloc(size);
}
void operator delete[](void *p)
{
cout<<"delete[] "<<endl;
free(p);
}
int main()
{
int *p = new int;
delete p;
int *pa = new int[20];
delete []pa;
A * cp = new A;
delete cp;
A * cpa = new A[20];
delete []cpa;
return 0;
}
B、類成員函數重載
#include <iostream>
#include <stdlib.h>
using namespace std;
class A
{
public:
A()
{
cout<<"A constructor"<<endl;
}
~A()
{
cout<<"A destructor"<<endl;
}
void * operator new (size_t size)
{
cout<<"new "<<size<<endl;
return malloc(size);
}
void operator delete(void *p)
{
cout<<"delete"<<endl;
free(p);
}
void * operator new[] (size_t size)
{
cout<<"new[] "<<size<<endl;
return malloc(size);
}
void operator delete[](void *p)
{
cout<<"delete[] "<<endl;
free(p);
}
private:
int a;
};
int main()
{
// int *p = new int;
// delete p;
// int *pa = new int[20];
// delete []pa;
A * cp = new A;
delete cp;
A * cpa = new A[20];
delete []cpa;
return 0;
}
編譯器默認為每個類重載了賦值操作符,但默認的賦值操作符只完成淺拷貝。與拷貝構造函數一樣,當需要深拷貝時需要顯示重載賦值操作符。
#include <iostream>
using namespace std;
class Test
{
private:
int* pointer;
public:
Test()
{
pointer = NULL;
}
Test(int n)
{
pointer = new int(n);
}
Test(const Test& another)
{
pointer = new int(*another.pointer);
}
Test& operator=(const Test& another)
{
if(this != &another)
{
delete pointer;
pointer = new int(*another.pointer);
}
return *this;
}
void print()
{
cout << this << endl;
}
};
int main()
{
Test t1 = 1;
Test t2 = t1;
Test t3;
t3 = t2;
t1.print();
t2.print();
t3.print();
return 0;
}
下標訪問操作符([])只能通過類的成員函數重載,并且重載函數只能使用一個參數。
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
class Test
{
int m_array[5];
public:
//使用位置索引作為下標訪問
int& operator [](int index)
{
return m_array[index];
}
//使用字符串作為下標訪問
int& operator [](const char* index)
{
if(index == "1st")
{
return m_array[0];
}
if(index == "2nd")
{
return m_array[1];
}
if(index == "3rd")
{
return m_array[2];
}
if(index == "4th")
{
return m_array[3];
}
if(index == "5th")
{
return m_array[4];
}
return m_array[0];
}
int length()const
{
return sizeof(m_array)/sizeof(int);
}
};
int main(int argc, char *argv[])
{
Test test;
for(int i = 0; i < test.length(); i++)
{
test[i] = i;
}
for(int i = 0; i < test.length(); i++)
{
cout << test[i] << endl;
}
cout << test["1st"] << endl;
cout << test["2nd"] << endl;
cout << test["3rd"] << endl;
cout << test["4th"] << endl;
cout << test["5th"] << endl;
return 0;
}
本文題目:C++語言學習(八)——操作符重載
當前URL:http://m.newbst.com/article14/ijphde.html
成都網站建設公司_創新互聯,為您提供電子商務、網站策劃、網頁設計公司、網站排名、網站制作、軟件開發
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯