经过operator关键字能够定义特殊的函数,operator本质是经过函数重载操做符。ios
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;代码也是合法的。ide
将操做符重载函数定义为类成员函数。函数
#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-实例:ui
#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; };
函数形式this
istream & operator>>(istream &,自定义类&); ostream & operator<<(ostream &,自定义类&);
流输入输出运算符重载经过友元来实现,避免修改C++的标准库。
operator<< 和operator>>实例:spa
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;
}