运算符重载是面向对象程序设计中最令人兴奋的特性之一。它能将复杂而晦涩的程序边的更为直观。运算符重载增强了c++语言的可扩充性。一般来说,运算符重载允许一个大的运算符集,其目的是提供用自然方式扩充语言。
一般来说a+b=c;只对基本类型有效,对于表达式
a=3+4;
a=“abc”+“bcd”
都是正确的,在这里同一个运算符“+”,由于所操作的数据类型不同而具有不同的意义,这就是运算符重载,而且是系统预先定义的运算符重载。运算符重载就是赋予已有的运算符多重含义。
(1).重载运算符必须符合语言语法;
eg:float f;
3.14=f;(错误)
(2).不能重载对内部c++数据类型进行操作的运算符
例如,不能重载二元浮点剑法运算符。
(3).不能创建新的运算符
(4).不能重载下面的运算符
.类成员选择符
.*成员指针运算符
::作用运算符
?:条件表达式运算符
除此之外的运算符都可以被重载,并且只有“=”的重载函数不能被继承;
(5)重载运算符要保持原有的基本语义不变。
因为运算重载是一个函数,所以运算符的重载实际上是函数的重载。编译程序对运算符重载的选择,是遵循函数重载的选择原则。
每个运算符的操作数都是语言规定好的,当金星运算符重载的时候,也必须遵循这个规定。当在类中重载操作符是,为使运算符函数能够访问类中声明的私有成员,那么运算符函数就必须被重载为非静态成员函数,或友元函数。
格式:<返回类型>operator<运算符>(参数列表)
由于每个非静态成员函数都带有一个隐含的自引用参数this指针,对于一元运算符函数,不用显示声明形参。所需要的形参将由自引用参数提供。而对于二元运算符函数,只需显示声明右操作数,左操作数则由自引用参数提供。总之,用成员函数重载运算符需要的参数的个数总比它的操作数少一。
例:
#include
using namespace std;
class Complex
{
public:
Complex(double r = 0.0, double i = 0.0);
Complex operator + (Complex c); //重载二元加;
Complex operator - (Complex c); //重载二元减;
void display();
private:
double real, imag; //这个是复数类,python中有这个函数,后面更新python的时候会介绍;
};
Complex::Complex(double r, double i)
{
real = r;
imag = i;
}
//重载加法二元函数;
Complex Complex::operator + (Complex c)
{
Complex temp;
temp.real = real + c.real;
temp.imag = imag + c.imag;
return temp;
}
Complex Complex::operator - (Complex c)
{
Complex temp;
temp.real = real - c.real;
temp.imag = imag - c.imag;
return temp;
}
void Complex::display()
{
const char *str; //前面必须加const,否则无法将X型赋给char*类型;
str = (imag < 0) ? "" : "+";
cout << real << str << imag << "i" << endl;
}
int main()
{
Complex c1(12.4, 13.3), c2(14.4, 25.6);
Complex c;
cout << "c1=";
c1.display();
cout << "c2=";
c2.display();
c = c1 + c2; //c=c1.operator+(c2);
cout << "c1+c2=";
c.display();
c = c1 - c2; //c=c1.operator-(c2);
cout << "c1-c2=";
c.display();
return 0;
}
用友元函数重载运算符的原型为:
friend<返回值类型>operator<运算符>(形参);
其中标识符的含义与成员函数重载运算符的格式中的同名标识符的含义相同。由于友元函数不是类的成员,所以没有this指针,所以参数的个数都必须声明。
例:
#include
using namespace std;
class Complex
{
public:
Complex(double r = 0, double i = 0);
friend Complex operator + (Complex c1, Complex c2); //重载二元加;
friend Complex operator-(Complex c1, Complex c2); //重载二元减;
void display();
private:
double real, imag;
};
Complex::Complex(double r, double i)
{
real = r;
imag = i;
}
Complex operator+(Complex c1, Complex c2)
{
Complex temp;
temp.imag = c1.imag + c2.imag;
temp.real = c1.real + c2.real;
return temp;
}
Complex operator-(Complex c1, Complex c2)
{
Complex temp;
temp.real = c1.real - c2.real;
temp.imag = c1.imag - c2.imag;
return temp;
}
void Complex::display()
{
const char* str;
str = (imag < 0) ? "" : "+";
cout << real << str << imag << "i" << endl;
}
int main()
{
Complex c1(12.4, 13.3), c2(14.4, 26.5);
Complex c;
cout << "c1=";
c1.display();
cout << "c2=";
c2.display();
c = c1 + c2;
cout << "c1+c2=";
c.display();
c = c1 - c2;
cout << "c1-c2=";
c.display();
return 0;
}
在许多情况下,用友元函数还是成员函数重载运算符在功能上没有什么区别,有时将二元运算符重载为友元函数比重载为成员函数使用起来灵活。例如:
c=34.4+c1;
如果使用“+”成员函数重载,会报错,因为该语句右边的表达式被解释为
34.5.operator+(c1);
当然,重载为友元函数也有一些限制。
第一:为保持与c++中规定的赋值语句语义一样,赋值运算符不能重载为友元函数,同理,“+=,-="等赋值运算符重载为成员运算符。
第二:友元函数不能重载”()[ ]和->"运算符。
第三:在重载增量或减量运算符时,若使用友元重载,则需要引用参数;
c++中有三种运算符,一种是单目运算符,双目运算符,还有就是三目运算符(条件运算符)。条件运算符不能被重载,前面介绍了双目运算符重载问题,现在我们介绍一下单目运算符的重载。
单目重载运算符和双目运算符重载有很多相似地方,主要就是参数个数不同,下面我们以“++”和“–”运算符作为例子来实现:
以成员函数方式重载前缀“++”运算符的原型的一般格式:
《返回类型》::operator++()
以类成员方式重载后缀“++”运算符的原型的格式:
<返回类型>::operator++
其中给出了一个int参数表明调用该函数是运算符“++”应放在操作数的后面,且参数本身在函数体中并不被使用,因此没有给出参数名字。
#include
using namespace std;
class Counter
{
public:
Counter() { value = 0; }
Counter(int i) { value = i; }
Counter operator++(); //前缀++运算符
Counter operator++(int); //后缀++运算符
Counter operator--(); //后缀--运算符;
Counter operator --(int); //后缀--运算符;
inline void display()
{
cout << value << endl;
}
private:
unsigned value;
};
//前缀++运算符的重载;
Counter Counter::operator++()
{
value++;
return *this;
}
//后缀++运算符的重载;
Counter Counter::operator++(int)
{
Counter temp;
temp.value = value++;
return temp;
//value++;
//return *this;
}
//前缀--运算符的重载;
Counter Counter::operator--()
{
value--;
return *this;
}
//后缀--运算符的重载;
Counter Counter::operator--(int)
{
Counter temp;
temp.value = value--;
return temp;
}
int main()
{
Counter n(10), c;
c = ++n;
cout << "前缀++运算符的计算结果:" << endl;
cout << "n= ", n.display();
cout << "c= ", c.display();
c = n++;
cout << "后缀++运算符计算结果:" << endl;
cout << "n= ", n.display();
cout << "n= ", c.display();
c = --n;
cout << "前缀运算符--计算结果:" << endl;
cout << "n= ", n.display();
cout << "c= ", c.display();
c = n--;
cout << "后缀运算符的计算结果:" << endl;
cout << "n= ", n.display();
cout << "c= ", c.display();
return 0;
}
友元函数实现单目运算符重载和双目运算符重载有异曲同工之处。只不过,由于友元函数没有this指针,而又不能改变++运算符的愿意,所以只能通过引用的方式,传参给指定的参数。
用友元函数重载前缀++运算符:
<类型>operator++(类名&);
用友元函数重载后缀++运算符:
<类型>operator++(类名&,int)
“–”运算符和++运算符重载方式相似,通过下面的程序来认识他们:
#include
using namespace std;
class Counter
{
public:
Counter() { value = 0; }
Counter(int i) { value = i; }
friend Counter operator++(Counter&); //++运算符前缀重载;
friend Counter operator++(Counter&, int); //++运算符后缀重载;
friend Counter operator--(Counter&);
friend Counter operator--(Counter&, int);
void display() { cout << value << endl; }
private:
unsigned value;
};
Counter operator++(Counter& p) //最前面的counter是函数返回类型;
{
p.value++;
return p;
}
Counter operator++(Counter& p, int)
{
Counter temp;
temp = p.value++;
return temp;
}
Counter operator--(Counter&p)
{
p.value--;
return p;
}
Counter operator--(Counter& p, int)
{
Counter temp;
temp.value = p.value--;
return temp;
}
int main()
{
Counter n(10), c;
c = ++n;
cout << "前缀++运算符的计算结果:" << endl;
cout << "n= ", n.display();
cout << "c= ", c.display();
c = n++;
cout << "后缀++运算符计算结果:" << endl;
cout << "n= ", n.display();
cout << "n= ", c.display();
c = --n;
cout << "前缀运算符--计算结果:" << endl;
cout << "n= ", n.display();
cout << "c= ", c.display();
c = n--;
cout << "后缀运算符的计算结果:" << endl;
cout << "n= ", n.display();
cout << "c= ", c.display();
return 0;
}
如果一个类的数据成员中有指向动态分配空间的指针,那么通常情况下应该定义拷贝构造函数,并重载赋值运算符,否则会出现运行错误。
下面我们来看一下一段代进行分析:
#include
using namespace std;
class Namelist
{
char* name;
public:
Namelist(int size)
{
name = new char[size];
}
//析构函数;
~Namelist()
{
delete[]name;
}
};
int main()
{
Namelist n1(10), n2(10);
n2 = n1;
return 0;
}
这个时候运行程序,你会发现程序无法运行。
那么为什么会出现这种情况?
首先构造函数以new动态分配存储块给对象n1和n2,然后把右边对象n1的值赋给n2,从而实现了类对象的赋值。如下图:
但是在最后,n2被析构函数清除后,此时堆区的模块就被收回堆区,但是在n1被析构函数删除的时候,,析构函数会继续寻找模块,所以会冲突。导致报错。这个时候,我们就可以通过重载默认赋值函数和拷贝构造函数解决。
Classname&Classname::operator=(Clasname obj)
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
class Namelist
{
char* name;
public:
Namelist(char *p)
{
name = new char[strlen(p) + 1];
if (name != 0)
strcpy(name, p);
}
//析构函数;
~Namelist()
{
delete [] name;
}
Namelist& operator=(char* p);
Namelist& operator=(Namelist&);
void display() { cout << name << endl; }
};
//重载赋值运算符,完成常量给对象赋值;
Namelist& Namelist::operator=(char* p)
{
name = new char[strlen(p) + 1];
if (name != 0)
strcpy(name, p);
return *this;
}
//重载赋值运算符,完成类对象之间的赋值;
Namelist& Namelist::operator=(Namelist& a) //设置为一个参数的原因和前面=的重载原因一样,有this指针;
{
if (this != &a)
{
delete name; //删除原有对象的类容;
name = new char[strlen(a.name) + 1];
strcpy(name, a.name);
}
return*this;
}
int main()
{
Namelist n1("I like you"), n2("bei jing li gong da xue");
cout << "赋值前的数据:" << endl;
n1.display();
n2.display();
cout << "赋值后的数据:" << endl;
n1 = n2;
n1.display();
n2.display();
return 0;
}
重载赋值运算符时,通常是返回调用该运算符对象的引用,不过不能使用引用返回一个局部对象,单this可以解决这个问题。只要非静态成员函数在运行,那么this指针就在作用内。
就像上面的代码一样,只需要一个形参既可以,这也就是this指针的应用。
拷贝构造函数和运算符重载函数都是用来拷贝一个类的对象给另一个同类型的对象。要注意拷贝构造函数于赋值重载运算符的使用区别。
(1).拷贝构造函数是用已存在对象的各成员的当前值来创建一个新的对象,在下面的三种情况下,系统会自动调用拷贝构造函数。
1.当说明新的类对象的同时,要给它赋值另一个当前已经存在的对象的当前值
2.当对象作为函数的赋值参数而对函数进行调用要进行实参和形参的结合时
3.当函数的返回值是类的对象,载函数调用结束后返回到主调函数处时。
(2).赋值运算符重载函数要把一个已存在对象的各成员当前赋值给另一个已存在的同类对象。
eg:
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
class Namelist
{
char *name;
public:
Namelist(char* p)
{
name = new char[strlen(p) + 1];
if (name != 0) //在c++中,0也就表示空;
{
strcpy(name, p);
}
}
Namelist() { };
Namelist(Namelist&);
Namelist& operator=(const char* p);
Namelist& operator=(Namelist&);
void display() {
if(name)
cout << name << endl;
}
~Namelist()
{
delete []name;
}
};
Namelist::Namelist(Namelist& a) //定义拷贝构造函数;
{
name = new char[strlen(a.name) + 1];
if (name != NULL)
{
strcpy(name, a.name);
}
}
//第一个赋值运算符重载函数,完成常量的赋值;
Namelist& Namelist::operator=(const char* p)
{
name = new char[strlen(p) + 1];
if (name != NULL)
{
strcpy(name, p);
}
return *this;
}
//第二个赋值运算符重载函数,完成对象之间的赋值;
Namelist& Namelist::operator=(Namelist& p)
{
if (this != &p)
{
delete[]name;
name = new char[strlen(p.name) + 1];
if(name!=NULL)
strcpy(name, p.name);
}
return *this;
}
int main()
{
Namelist n1("first object"), n2("second object"), n3;
cout << "赋值前的数据:" << endl;
n1.display();
n2.display();
n3 = "third obkect"; //调用第一个赋值运算符重载函数;
n2 = n1; //调用第二个赋值运算符重载函数;
Namelist n4(n2); //调用拷贝构造函数;
cout << "赋值后的函数值:" << endl;
n1.display();
n2.display();
n3.display();
n4.display();
return 0;
}
对下标运算符“[ ]”重载的时候只能重载为成员函数,不可重载为友元函数。若在某自定义类中重载了下标运算符,则可将该类的类对象当作一个“数组”。从而对该类对象通过使用下标的方式来访问其中成员的数据,各下标变量的具体取值于类对象数据成员间 的对应关系完全由程序员在重载函数中设计和规定。
例如,重载下标运算符,访问数组原素,进行越界检查
#include
#include //函数调度头文件,在使用进程终止函数的时候使用。
using namespace std;
const int LIMT = 100; //定义一个常量
class Intarray
{
private:
int size; //数组大小;
int* array; //数组名;
public:
Intarray(int = 1); //默认为一个元素数组;
int& operator[](int n);
~Intarray();
};
Intarray::Intarray(int i)
{
//数组越界检查;
if (i<0||i>LIMT)
{
cout << "out of array limit" << endl;
exit(1);
}
size = i;
array = new int[size];
}
int& Intarray::operator[](int n)
{
//下标越界检查;
if (n < 0 || n >= size)
{
cout << "out of range" << endl;
exit(1);
}
return array[n];
}
Intarray::~Intarray()
{
delete[]array;
size = 0;
}
int main()
{
int k, num;
cout << "please input size of array(1~100):";
cin >> k;
Intarray array(k);
for (int j = 0; j < k; j++)
{
array[j] = j * 10;
}
cout << "please input number of output array(1~100):";
cin >> num;
for (int j = 0; j < num; j++)
{
int temp = array[j];
cout << "Element" << "array[" << j << "]" << "is" << temp << endl;
}
return 0;
}
他的格式基本和前面学的运算符重载的格式相同;就不过多介绍了。他的重载方式和“【】”运算重载的方式相同,只能通过成员函数的方式重载。
#include
using namespace std;
class Func
{
private:
double X, Y, Z;
public:
double GetX() { return X; }
double GetY() { return Y; }
double GetZ() { return Z; }
double operator() (double x, double y, double z);
};
double Func::operator()(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
return 5 * x + 6 * y - 7 * z + 8;
}
int main()
{
Func f;
f(3.2, 4.5, 5.6); //f.operator()_(3.2,4.5,5.6)
cout << "func(";
cout << f.GetX() << "," << f.GetY() << "," << f.GetZ() << ")=";
cout << f(3.2, 4.5, 5.6) << endl;
return 0;
}
下面我们重载函数访问二维数组元素,bing进行越界检查;
eg:
#include
#include
using namespace std;
const int LIMIT = 100;
class Intarray
{
private:
int size1; //行数;
int size2; //列数;
int* array; //数组名;
public:
Intarray(int = 1, int = 1); //默认数组1行1列;
int& operator() (int i, int j);
~Intarray();
};
Intarray::Intarray(int i, int j)
{
//数组越界检查;
if ((i<0 || i>LIMIT) || (j<0 || j>LIMIT))
{
cout << "out of array limit" << endl;
exit(1);
}
size1 = i;
size2 = j;
array = new int[size1 * size2];
}
int& Intarray::operator()(int m, int n)
{
//下标越界检查;
if ((m < 0 || m >= size1) || (n < 0 || n >= size2))
{
cout << "out of range" << endl;
exit(1);
}
return array[m * size1 + n];
}
Intarray::~Intarray()
{
delete[]array;
size1 = 0;
size2 = 0;
}
int main()
{
int r, c, m, n, i, j;
cout << "please input row&&col of array(1~100):";
cin >> r >> c;
Intarray array(r, c);
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
array(i, j) = 2 * i + j;
}
}
cout << "please input row&&col numbers of output array(1~100):";
cin >> m >> n;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j ++ )
{
int temp = array(i, j);
cout << "Element";
cout << "array[" << i << "," << j << "]" << "is " << temp << endl;
}
return 0;
}
}
在c++中,类是用户自定义的类型,和c语言中的结构体类似。类与类之间,类与基本数据类型之间都可以实现数据类型的转换。实现这种转换需要使用构造函数和类类型转换运算符。
利用构造函数实现从基本类型到类类型的转换。前提是类中一定要具有只有一个非默认参数的构造函数。
如上面的程序就是利用了构造函数实现类型的转换。
格式:
operator<返回类型名>()
{
…
return <基本数值类型>;
}
#include
using namespace std;
class Type
{
public:
Type(int a, int b = 1); //只有一个非默认参数的构造函数;
operator double();
private:
int data1, data2;
};
Type::Type(int a, int b)
{
data1 = a;
data2 = b;
}
Type::operator double()
{
return double(data1) / double(data2);
}
int main()
{
Type c1(2, 4), c2(3, 8);
cout << "c1=" << c1 << "c2=" << c2 << endl;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
class Vector
{
private:
int size; //数组大小
int* array;//数组名;
public:
Vector(int = 1); //构造函数,默认为一个元素数组;
Vector(Vector& a); //拷贝构造函数;
int& operator[](int n);
Vector& operator+(Vector c); //成员函数形式重载;
Vector& operator-(Vector c); //重载-;
Vector& operator=(Vector& a);
int operator()();
~Vector();
};
Vector::Vector(int i)
{
size = i;
array = new int[size];
for (int i = 0; i < size; i++)
{
array[i] = 0;
}
}
int& Vector::operator[](int n)
{
if (n < 0 || n >= size)
{
cout << "erro" << endl;
exit(1);
}
return array[n];
}
Vector temp(4);
Vector& Vector::operator+(Vector c)
{
for (int i = 0; i < 4; i++)
{
temp[i] = 0;
}
for (int i = 0; i < 4; i++)
{
temp[i] = array[i] + c.array[i];
}
return temp;
}
Vector& Vector::operator-(Vector c)
{
for (int i = 0; i < 4; i++)
{
temp[i] = 0;
}
for (int i = 0; i < 4; i++)
{
temp.array[i] = array[i] - c.array[i];
}
return temp;
}
Vector::Vector(Vector& a)
{
array = new int[size];
if (array != 0)
{
for (int i = 0; i < 4; i++)
{
array[i] = a.array[i];
}
}
}
Vector& Vector::operator=(Vector& a)
{
if (this != &a)
{
delete[]array;
array = new int[size];
if (array != 0)
{
for (int i = 0; i < 4; i++)
{
array[i] = a.array[i];
}
}
}
return *this;
}
int Vector::operator()()
{
return size;
}
Vector::~Vector()
{
delete[]array;
size = 0;
}
int main()
{
int j, length;
Vector X(4), Y(4), Sum(4), Sub(4);
for (j = 0; j < 4; j++)
{
X[j] = j + 2;
Y[j] = j * 2;
}
cout << "first vector=(";
for (j = 0; j < 4; j++)
{
int temp = X[j];
if (j > 0)
{
cout << ",";
}
cout << temp;
}
}
cout << ")" << endl;
cout << "second vector=(";
for (j = 0; j < 4; j++)
{
int temp = Y[j];
if (j > 0)
{
cout << ",";
}
cout << temp;
}
cout << ")" << endl;
Sum = X + Y;
Sub = X - y;
cout << "sum=(";
for (j = 0; j < 4; j++)
{
int temp = Sum[j];
if (j > 0)
cout << ",";
cout << temp;
}
cout << ")";
cout << "Sub=(";
for (j = 0; j < 4; j++)
{
int temp = Sub[j];
if (j > 0)
{
cout << ",";
}
cout << temp;
}
cout << ")" << endl;
length = X();
cout << "length of Vector is" << length << endl;
return 0;