文章参考:
1. 概述
如果不进行特殊处理,C++默认的运算符只能对基本的常量或变量进行运算,不能用于对象之间的运算。但有时我们希望对这些运算符功能进行拓展,让他们能够支持更多的运算。运算符重载应运而生。它能够赋予已有的运算符多重含义,使得同一运算符面对不同类型的数据产生不同的效果。
格式如下:
返回值 opetator 运算符 (形参表)
{
...
}
包含被重载的运算符的表达式会被编译为对运算符函数的调用,运算符的操作数称为函数的实参,运算的结果就是函数的返回值。运算符可以多次重载。
运算符可以被重载为全局函数,也可以重载为成员函数。一般建议重载为成员函数,这样可以更好的体现运算符和类的关系。下面是一个例子:
-
代码:
#include <iostream> #include <cstring> using namespace std; class Suqare{ public: int _w; int _l; public: Suqare(){} Suqare(int w, int l): _w(w), _l(l){} ~Suqare(){} // 成员函数操作符重载 Suqare operator + (const Suqare &s); }; Suqare Suqare::operator+(const Suqare &s){ return Suqare(this->_w + s._w, this->_l + s._l); } // 定义一个全局操作符重载函数 Suqare operator - (const Suqare &s1, const Suqare &s2){ return Suqare(s1._w - s2._w, s1._l - s2._l); } int main(void){ Suqare s1(1,2), s2(2,4), s3; s3 = s1 + s2; cout << s3._w << "," s3._l << << endl; s3 = s2 - s1; cout << s3._w << "," s3._l << << endl; return 0; }
-
输出:
3,6 1,2
-
结论:从上例可以看出,如果是成员函数进行操作符重载,那么形参数量可以减一,因为可以通过
this
来获取当前对象本身的属性。
2. 赋值运算符重载
可以对赋值运算符=
进行重载,从而对对象进行快速赋值。c++中规定,“=”只能重载为成员函数
。
EG:
-
代码:实现字符串的拷贝功能
#include <iostream> #include <cstring> using namespace std; class String { private: char * str; public: String() :str(NULL) { } // 最后一个const表示这是一个常函数,避免对变量进行修改。第一个const表示返回的字符串只读,也不许外界修改。 const char * c_str() const { return str; }; String & operator = (const char * s); ~String(); }; String & String::operator = (const char * s) //重载"="以使得 obj = "hello"能够成立 { if (str) delete[] str; if (s) { //s不为NULL才会执行拷贝 str = new char[strlen(s) + 1]; strcpy(str, s); } else str = NULL; return *this; } String::~String() { if (str) delete[] str; }; int main() { String s; s = "Good Luck,"; //等价于 s.operator=("Good Luck,"); cout << s.c_str() << endl; // String s2 = "hello!"; //这条语句要是不注释掉就会出错 s = "Shenzhou 8!"; //等价于 s.operator=("Shenzhou 8!"); cout << s.c_str() << endl; return 0; }
-
输出:
Good Luck, Shenzhou 8!
3. 深拷贝和浅拷贝
浅拷贝:
当我们使用=
,通过一个已经初始化好的对象对另一个对象进行赋值时,对于基础的数据类型,将会采用复制的操作。而对于如数组、指针等指向一片空间的区域,采用的是复制地址而不是复制地址处的内容,这就是浅拷贝。显然,当涉及到内存操作时,浅拷贝可能会出现注入两个指针指向同一空间的情况,这会导致在销毁对象时对同一空间的多次释放,从而引发错误。
深拷贝:
通过重载=
赋值操作符,我们可以对默认的赋值方式进行修改,从而实现在对指针类变量赋值时额外再开辟一片内存,并将内容拷贝过去,而不是只拷贝地址。这样就能避免重复释放空间的错误。
4. 运算符重载为友元函数
有时候我们不想对成员函数进行运算符重载,全局函数又无法直接访问类的非共有成员,这时就可以使用友元函数。
EG:
-
代码:
#include <iostream> using namespace std; class Complex{ private: double real, imag; public: Complex(double r = 0.0, double i = 0.0): real(r), imag(i) { } friend Complex operator+(Complex& a, Complex& b) { Complex temp; temp.real = a.real + b.real; temp.imag = a.imag + b.imag; return temp; } void display() { cout << real; if (imag > 0) cout << "+"; if (imag != 0) cout << imag << "i" << endl; } }; int main() { Complex a(2.3, 4.6), b(3.6, 2.8), c; a.display(); b.display(); c = a + b; c.display(); c = operator+(a, b); c.display(); return 0; }
-
输出:
2.3+4.6i 3.6+2.8i 5.9+7.4i 5.9+7.4i
5. 运算符重载实现可变长数组
在C++中,我们可以通过通过运算符重载实现一个可变长数组类。它拥有以下特点:
- 初始化对象是指定数组的元素数量。
- 可以动态添加数组。
- 自动完成内存的分配和动态释放问题。
- 可以正常访问下标。
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
class CArray
{
int size; //数组元素的个数
int* ptr; //指向动态分配的数组
public:
CArray(int s = 0); //s代表数组元素的个数
CArray(CArray & a);
~CArray();
void push_back(int v); //用于在数组尾部添加一个元素 v
CArray & operator = (const CArray & a); //用于数组对象间的赋值
int length() const { return size; } //返回数组元素个数
int & operator[](int i)
{ //用以支持根据下标访问数组元素,如“a[i]=4;”和“n=a[i];”这样的语句
return ptr[i];
};
};
CArray::CArray(int s) : size(s)
{
if (s == 0)
ptr = NULL;
else
ptr = new int[s];
}
CArray::CArray(CArray & a)
{
if (!a.ptr) {
ptr = NULL;
size = 0;
return;
}
ptr = new int[a.size];
memcpy(ptr, a.ptr, sizeof(int) * a.size);
size = a.size;
}
CArray::~CArray()
{
if (ptr) delete[] ptr;
}
CArray & CArray::operator=(const CArray & a)
{ //赋值号的作用是使 = 左边对象中存放的数组的大小和内容都与右边的对象一样
if (ptr == a.ptr) //防止 a=a 这样的赋值导致出错
return *this;
if (a.ptr == NULL) { //如果a里面的数组是空的
if (ptr)
delete[] ptr;
ptr = NULL;
size = 0;
return *this;
}
if (size < a.size) { //如果原有空间够大,就不用分配新的空间
if (ptr)
delete[] ptr;
ptr = new int[a.size];
}
memcpy(ptr, a.ptr, sizeof(int)*a.size);
size = a.size;
return *this;
}
void CArray::push_back(int v)
{ //在数组尾部添加一个元素
if (ptr) {
int* tmpPtr = new int[size + 1]; //重新分配空间
memcpy(tmpPtr, ptr, sizeof(int) * size); //复制原数组内容
delete[] ptr;
ptr = tmpPtr;
}
else //数组本来是空的
ptr = new int[1];
ptr[size++] = v; //加入新的数组元素
}
int main()
{
CArray a; //开始的数组是空的
for (int i = 0; i<5; ++i)
a.push_back(i);
CArray a2, a3;
a2 = a;
for (int i = 0; i<a.length(); ++i)
cout << a2[i] << " ";
a2 = a3; //a2 是空的
for (int i = 0; i<a2.length(); ++i) //a2.length()返回 0
cout << a2[i] << " ";
cout << endl;
a[3] = 100;
CArray a4(a);
for (int i = 0; i<a4.length(); ++i)
cout << a4[i] << " ";
return 0;
}
输出:
0 1 2 3 4
0 1 2 100 4
其中,[]
是双目运算符,a[i]
等价于a.operator[](i)
。
6. C++重载输入输出运算符
6.1 输出<<
在C++中,左移运算符<<
被和cout
一起用作输出,左移运算符本身并没有这种效果,之所以能做到这一步,是因为它被重载了。
cout
是ostream
的对象,它们都是在头文件<iostream>
中被声明的。ostream将<<进行了重载,并且为了适应多种类型的输出,将其重载了多次,例如:
-
输出字符串:
ostream & ostream::operator << (const char *s) { // 输出s的代码 return *this; }
-
输出整形:
ostream & ostream::operator << (int n) { // 输出整形n的代码 return *this; }
重载函数的返回类型是ostream
的引用,依旧是ostream类型,因此我们可以链式调用重载函数,例如:
cout << "this is a string" << 5;
// 等价于
(cout.operator<<("this is a string")).operator<<(5);
6.2 输入>>
和输出类似,右移运算符>>
在istream
类中被多次重载,并通过istream
的对象cin
进行调用,这些也都是定义在头文件<iostream>
中的。一个重载案例是:
istream & istream::operator << (const char *s)
{
// 输入s的代码
return *this;
}
6.3 案例
问题:
现在有一个Complex类,假定 c 是 Complex 复数类的对象,现在希望写cout<<c;
就能以 a+bi 的形式输出 c 的值;写cin>>c;
就能从键盘接受 a+bi 形式的输入,并且使得 c.real = a, c.imag = b。
分析:
显然,需要对ostream和istream进行重载,但是这两个类在标准头文件中,无法修改,因此只能使用全局函数来进行运算符重载。此外,如果使用全局函数,想要访问到Complex的数据成员,需要将其声明为友元函数。
代码:
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
class Complex
{
double real,imag;
public:
Complex( double r=0, double i=0):real(r),imag(i){ };
friend ostream & operator<<( ostream & os,const Complex & c);
friend istream & operator>>( istream & is,Complex & c);
};
ostream & operator<<( ostream & os,const Complex & c)
{
os << c.real << "+" << c.imag << "i"; //以"a+bi"的形式输出
return os;
}
istream & operator>>( istream & is,Complex & c)
{
string s;
is >> s; //将"a+bi"作为字符串读入, "a+bi" 中间不能有空格
int pos = s.find("+",0);
string sTmp = s.substr(0,pos); //分离出代表实部的字符串
c.real = atof(sTmp.c_str());//atof库函数能将const char*指针指向的内容转换成 float
sTmp = s.substr(pos+1, s.length()-pos-2); //分离出代表虚部的字符串
c.imag = atof(sTmp.c_str());
return is;
}
int main()
{
Complex c;
int n;
cin >> c >> n;
cout << c << "," << n;
return 0;
}
输出:
13.2+133i 87
13.2+133i,87
7. 强制类型转换重载
在C++中,类的名字本身也是一种运算符,也就是类型强制转换运算符。需要注意的是:
- 类型强制运算符是单目运算符,只能被重载为成员函数,不能重载为全局函数。
EG:
-
代码:
#include <iostream> using namespace std; class Complex { double real, imag; public: Complex(double r = 0, double i = 0) :real(r), imag(i) {}; operator double() { return real; } //重载强制类型转换运算符 double }; int main() { Complex c(1.2, 3.4); cout << (double)c << endl; //输出 1.2 double n = 2 + c; //等价于 double n = 2 + c. operator double() cout << n; //输出 3.2 }
-
输出:
1.2 3.2
8. C++重载++和--运算符
自增运算符++
、自减运算符--
都可以被重载,但是它们有前置、后置之分。
以++
为例,假设 obj 是一个 CDemo 类的对象,++obj
和obj++
本应该是不一样的,前者的返回值应该是 obj 被修改后的值,而后者的返回值应该是 obj 被修改前的值。如果如下重载++
运算符:、
CDemo & CDemo::operator ++ ()
{
//...
return * this;
}
那么不论obj++还是++obj,都等价于obj.operator++()无法体现出差别。
为了解决这个问题,C++ 规定,在重载++或--时,允许写一个增加了无用 int 类型形参的版本,编译器处理++或--前置的表达式时,调用参数个数正常的重载函数;处理后置表达式时,调用多出一个参数的重载函数。来看下面的例子:
#include <iostream>
using namespace std;
class CDemo {
private:
int n;
public:
CDemo(int i=0):n(i) { }
CDemo & operator++(); //用于前置形式
CDemo operator++( int ); //用于后置形式
operator int ( ) { return n; }
friend CDemo & operator--(CDemo & );
friend CDemo operator--(CDemo & ,int);
};
CDemo & CDemo::operator++()
{//前置 ++
n ++;
return * this;
}
CDemo CDemo::operator++(int k )
{ //后置 ++
CDemo tmp(*this); //记录修改前的对象
n++;
return tmp; //返回修改前的对象
}
CDemo & operator--(CDemo & d)
{//前置--
d.n--;
return d;
}
CDemo operator--(CDemo & d,int)
{//后置--
CDemo tmp(d);
d.n --;
return tmp;
}
int main()
{
CDemo d(5);
cout << (d++ ) << ","; //等价于 d.operator++(0);
cout << d << ",";
cout << (++d) << ","; //等价于 d.operator++();
cout << d << endl;
cout << (d-- ) << ","; //等价于 operator-(d,0);
cout << d << ",";
cout << (--d) << ","; //等价于 operator-(d);
cout << d << endl;
return 0;
}
输出:
5,6,7,7
7,6,5,5
标签:int,CDemo,运算符,operator,重载,ptr
From: https://www.cnblogs.com/beasts777/p/17875462.html