C++面向对象程序设计笔记:第四章 运算符重载
重载为成员函数时,参数个数为运算符目数减一。
重载为普通函数时,参数个数为运算符目数。
class Complex {
public:
double real, imag;
Complex(double r = 0.0, double i = 0.0) : read(r), imag(i){ }
Complex operator-(const Complex& c);
};
Complex operator+(const Complex& a, const Complex& b) { // 普通函数
return Complex(a.real + b.real, a.imag + b.imag);
}
Complex Complex::operator-(const Complex& c) { // 成员函数
return Complex(real - c.real, imag - c.imag);
}
/*
c = a + b; // 等价于c = operator+(a, b);
c = a - b; // 等价于c = a.operator(b);
*/
赋值运算符重载
只能重载为成员函数。
class String {
private:
char* str;
public:
String ():str(new char[1]) {str[0] = 0;}
~String() {delete[] str};
const char* c_str() {return str;}
String& operator=(const char* s);
};
String& String::operator=(const char* s) {
delete[] str;
str = new char[strlen(s) + 1]; // +1为了放下/0
strcpy(str, s);
return *this;
}
int main() {
String s;
s = "Good Luck,"; // 等价于s.operator=("Good Luck,");
// String s2 = "hello!"; // 出错,会执行构造函数,而不是赋值
return 0;
}
浅拷贝和深拷贝
class String;
int main() {
String S1, S2;
S1 = "this";
S2 = "that";
S1 = S2; // S1.str指针指向S2.str指向的位置
return 0;
}
如果不定义自己的赋值运算符,那么S1=S2实际上导致S1.str和S2.str指向同一个位置。
如果S1对象消亡,析构函数将释放S1.str指向的空间,则S2消亡时还要再释放一次,会出错。
另外,如果执行S1=”other”;会到导致S2.str指向的地方被delete掉。
因此,需要在class String里面添加成员函数
String& operator=(const String& s) {
delete[] str;
str = new char[strlen(s.str) + 1];
strcpy(str, s.str);
return *this.
}
如果执行s = s;会出错,需要做出修改。
String& operator=(const String& s) {
if(this == &s)
return *this;
delete[] str;
str = new char[strlen(s.str) + 1];
strcpy(str, s.str);
return *this.
}
为什么”operator=”返回值的类型是”String&”?
为了保留运算符原有的特性不变。
例如:
a=b=c; 其中(b=c)的返回值是b,相当于b=c;a=b;。
(a=b)=c; 相当于a=b;a=c;
为String类编写复制构造函数的时候,会面临和 = 同样的问题,用同样的方法处理。
String(String& s) {
str = new char[strlen(s.str) + 1];
strcpy(str, s.str);
}
运算符重载为友元函数
使用情况:
一般情况下,将运算符重载为类的成员函数,是较好的选择;
但是当重载为成员函数不能满足使用要求,重载为普通函数又不能访问类的私有对象,就需要将运算符重载为友元。
class Complex {
double real, imag;
public:
Complex(double r, double imag):real(r), iamg(i){ }
Complex operator+(double r);
};
Complex Complex::operator+(double r) {
return Complex(real + r, imag);
}
int main(){
Complex c;
c = c + 5;
c = 5 + c; // 编译错误
return 0;
}
需要对上面代码做出修改:
class Complex {
// ...
public:
friend Complex operator+(double r, const Complex& c);
}
Complex operator+(double r, const Complex& c) {
return Complex(c.real + r, c.imag);
}
流插入运算符的重载
流插入运算符其实是将”左移/右移”操作进行了重载。
cout是在iostream中定义的,ostream类的对象。
“<<”能用在cout上是因为iostream里对”<<”进行了重载。
实现cout<<5; 等价于cout.operator<<(5);
void ostream::operator<<(int n) {
.. // 输出n的代码
return;
}
实现cout<<5<<”this”; 等价于cout.operator<<(5).operator<<(“this”);
ostream& ostream::operator<<(int n) {
... // 输出n的代码
return *this;
}
ostream& ostream::operator<<(char* s) {
... // 输出s的代码
return *this;
}
不能为ostream添加成员函数,可以重载为全局函数。
ostream& operator<<(ostream& o, const CStudent& s) {
o << s.nAge;
return o;
}
重载类型转换运算符
class Compelx {
double real, iamg;
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 << "\n"; // 输出1.2(显式转换)
double n = 2 + c; // 等价于double n = 2 + c.operator double();(隐式转换)
cout << n;
}
自增、自减运算符
自增运算符++、自减运算符–有前置和后置之分,为了区分所重载的是前置运算符还是后置运算符,C++规定:
前置运算符作为一元运算符重载:
重载为成员函数:
T& operator++();
T& operator–();
重载为全局函数:
T1& operator++(T2);
T1& operator–(T2);后置运算符作为二元运算符重载,多写一个没用的参数:
重载为成员函数:
T& operator++(int);
T& operator–(int);
重载为全局函数:
T1& operator++(T2, int);
T1& operator–(T2, int);
注:在没有后置运算符重载而有前置重载的情况下,在vs中obj++也调用前置重载,而dev中会报错。
注:(a++)=1;不会使得a=1,因为(a++)返回的是一个临时的对象,而不是a的引用。
这就是为什么(++i)比(i++)快!
注意事项:
- 运算符重载不改变运算符的优先级。
- “.”, “.*”, “::”, “?:”, “sizeof”不允许被重载。
- 重载运算符()、[]、->或者赋值运算符=时,运算符重载必须声明为类的成员函数。
课后习题
014:MyString
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class MyString {
char * p;
public:
MyString(const char * s) {
if( s) {
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
}
~MyString() { if(p) delete [] p; }
void Copy(char* s) {
if(p) delete[] p;
if( s) {
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
}
friend ostream& operator<< (ostream &o, const MyString& s) {
if(s.p)
o << s.p;
return o;
}
MyString& operator=(const char* s) {
if(p) delete[] p;
if( s) {
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
return *this;
}
MyString& operator= (const MyString& str) {
if(p) delete[] p;
char* s = str.p;
if( s) {
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
return *this;
}
MyString (const MyString& str) {
if(p) delete[] p;
char* s = str.p;
if( s) {
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
}
};
int main()
{
char w1[200],w2[100];
while( cin >> w1 >> w2) {
MyString s1(w1),s2 = s1;
MyString s3(NULL);
s3.Copy(w1);
cout << s1 << "," << s2 << "," << s3 << endl;
s2 = w2;
s3 = s2;
s1 = s3;
cout << s1 << "," << s2 << "," << s3 << endl;
}
}
015:看上去好坑的运算符重载
#include <iostream>
using namespace std;
class MyInt
{
int nVal;
public:
MyInt( int n) { nVal = n ;}
MyInt& operator-(int n) {
nVal -= n;
return *this;
}
friend int Inc(MyInt& i) {
return i.nVal + 1;
}
};
int Inc(int n) {
return n + 1;
}
int main () {
int n;
while(cin >>n) {
MyInt objInt(n);
objInt-2-1-3;
cout << Inc(objInt);
cout <<",";
objInt-2-1;
cout << Inc(objInt) << endl;
}
return 0;
}
016:惊呆!Point竟然能这样输入输出
#include <iostream>
using namespace std;
class Point {
private:
int x;
int y;
public:
Point() { };
friend istream& operator>>(istream& in, Point& p) {
in >> p.x >> p.y;
return in;
}
friend ostream& operator<<(ostream& out, Point& p) {
out << p.x << "," << p.y;
return out;
}
};
int main()
{
Point p;
while(cin >> p) {
cout << p << endl;
}
return 0;
}
017:二维数组类
#include <iostream>
#include <cstring>
using namespace std;
class Array2 {
int** nums;
public:
Array2():nums(NULL) { }
Array2(const int n, const int m) {
nums = new int*[n];
for(int i = 0; i < n; i++)
nums[i] = new int[m];
}
int& operator ()(int i, int j) {
return nums[i][j];
}
int* operator [](int pos) {
return nums[pos];
}
};
int main() {
Array2 a(3,4);
int i,j;
for( i = 0;i < 3; ++i )
for( j = 0; j < 4; j ++ )
a[i][j] = i * 4 + j;
for( i = 0;i < 3; ++i ) {
for( j = 0; j < 4; j ++ ) {
cout << a(i,j) << ",";
}
cout << endl;
}
cout << "next" << endl;
Array2 b; b = a;
for( i = 0;i < 3; ++i ) {
for( j = 0; j < 4; j ++ ) {
cout << b[i][j] << ",";
}
cout << endl;
}
return 0;
}
018:别叫,这个大整数已经很简化了!
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
const int MAX = 110;
class CHugeInt {
int num[210];
public:
CHugeInt(char* s) {
memset(num, 0, sizeof num);
int n = strlen(s);
for(int i = 0; i < n; i++) {
num[n - i - 1] = s[i] - '0';
}
}
CHugeInt(int s_int) {
memset(num, 0, sizeof num);
string s = to_string(s_int);
int n = s.length();
for(int i = 0; i < n; i++) {
num[n - i - 1] = s[i] - '0';
}
}
CHugeInt(const CHugeInt& a) {
for(int i = 0; i < 210; i++)
num[i] = a.num[i];
}
CHugeInt operator+(const CHugeInt& a) {
CHugeInt b(0);
int tmp = 0;
for(int i = 0; i < 210; i++) {
int cur = num[i] + a.num[i] + tmp;
b.num[i] = cur % 10;
tmp = cur / 10;
}
return b;
}
CHugeInt& operator++() {
CHugeInt a(1);
int tmp = 0;
for(int i = 0; i < 210; i++) {
int cur = num[i] + a.num[i] + tmp;
num[i] = cur % 10;
tmp = cur / 10;
}
return *this;
}
CHugeInt operator++(int) {
CHugeInt res(*this);
CHugeInt a(1);
int tmp = 0;
for(int i = 0; i < 210; i++) {
int cur = num[i] + a.num[i] + tmp;
num[i] = cur % 10;
tmp = cur / 10;
}
return res;
}
friend CHugeInt& operator+=(CHugeInt& a, int b_int) {
CHugeInt b(b_int);
a = a + b;
return a;
}
friend CHugeInt operator+(CHugeInt& a, int b_int) {
CHugeInt b(b_int);
b = a + b;
return b;
}
friend CHugeInt operator+(int b_int, CHugeInt& a) {
CHugeInt b(b_int);
b = a + b;
return b;
}
friend ostream& operator<<(ostream& out, CHugeInt a) {
int len = 209;
while(len > 0 && a.num[len] == 0) {
if(a.num[len] != 0) break;
len--;
}
len++;
for(int i = 0; i < len; i++) {
out << a.num[len - 1 - i];
}
return out;
}
};
int main()
{
char s[210];
int n;
while (cin >> s >> n) {
CHugeInt a(s);
CHugeInt b(n);
cout << a + b << endl;
cout << n + a << endl;
cout << a + n << endl;
b += n;
cout << ++ b << endl;
cout << b++ << endl;
cout << b << endl;
}
return 0;
}