1.[ ]操作符重载
C++语言规定:“[ ]”只能作为类的成员函数进行重载。 “[ ]”是C++中的下标运算符,对于数组或指针来说,下表运算的语义是确定的,不能进行重载。因此,如果看到一个运算结果不是数组或指针的表达式后跟“[ ]”运算符,一定是对“[ ]”进行了重载。
一个例子:
#include <iostream>
using namespace std;
class A
{
int num[3];
public:
A();
int& operator[](int);
};
A::A()
{
num[0] = 1;
num[1] = 2;
num[2] = 3;
}
int& A::operator[](int sub)
{
cout<<"you are in\n";
if(sub < 0 || sub >2)
throw sub;
else
return num[sub];
}
int main()
{
A a;
A *p = &a;
try
{
for(int i = 0; i < 4; i++)
//cout<<a[i]<<endl; //这句和下面的作用一样。 这里也对[]进行了重载
cout<<p[0][i]<<endl; //第一个[]运算符是下标运算符的原意,p[0]相当于*p,代表指针所指向的对象
//第二个[]运算符使用的是重载后的语义,表示从对象内部的成员数组中取数据
}
catch(int sub)
{
cout<<"subscript out of range:"<<sub<<endl;
}
return 0;
};
对 [ ]操作符重载,一般会将操作符函数的返回值定义为引用类型。因为这样找到数组元素后,既可以对它进行读操作,也可以进行写操作。
一般来说,数组下标的数据类型是整数,这是C/C++语言中下标的基本用法。但从语法的角度来说,对operator [] 操作符函数进行重载,并没有限制下标的数据类型,这样就可以在某些特殊的情况下使用特殊的数据类型作为下标,看下面的一个例子,你就没白了。。。。
以字符串作为数组下标:
#include <iostream>
#include <string>
using namespace std;
class Employee
{
string name;
string position;
public:
Employee(string, string);
string& operator[](string);
};
Employee::Employee(string n, string p)
{
name = n;
position = p;
}
string& Employee::operator[](string s) //以字符串作为参数,而不是整型
{
if(s == "name")
return name;
else if(s == "position")
return position;
throw s;
}
int main()
{
Employee e1("忍者", "经理"), e2("核弹","职员");
try
{
cout<<"e1's name is:"<<e1["name"]<<endl; //重载了操作符
cout<<"e1's position is:"<<e1["position"]<<endl;
}
catch(string s)
{
cout<<"error: not find "<<s<<endl;
}
return 0;
}
总结一下 重在operator[] 要注意的几点:1.操作符函数operator[] 只接受一个参数,没有参数和多于一个参数都会造成编译错误,参数的类型可以是任何类型;
2.操作符函数operator[]的返回值类型应该是引用类型,这是为了与传统的数组下标运算语义保持一致;
3.当a为一个数组时,a[i]和i[a]都是合法的,都表示数组a 的下标为i的元素。但是,如果a是一个重载了operator[]的类的对象,那么
i[a]的表示方法将引发编译错误。所以为了显示的表明a是一个数组,在使用下标运算符的时候可采用i[a]的表示方式。
2. *操作符重载
*操作符既可以友元函数的形式重载,也可以以成员函数的形式重载,但如果是后者,应这样定义*操作符函数:
T operator*()
{
return *p;
} 一般情况下重载 * 操作符都是以成员函数的形式进行的。
"*"是一个一元操作符,它作用于指针,表示去指针所指单元的内容。一般来说,对*操作符进行重载的类都含有一个指针。
一个例子:
#include <iostream>
using namespace std;
template<typename T>
class Data
{
T *ptr;
public:
Data(T *p)
{
ptr = p;
}
~Data()
{
delete ptr;
}
//以友元函数形式重载
//template<typename T> friend T operator*(const Data<T>&);
//以成员函数形式重载,,这样比友元函数简洁多了
T operator*()
{
return *ptr;
}
};
//友元函数
//template<typename T>
//T operator*(const Data<T>& d)
//{
// return *(d.ptr);
//}
int main()
{
Data<int> intData(new int(67));
Data<double> doubleData(new double(22.2));
cout<<*intData<<endl; //重载了*操作符
cout<<*doubleData<<endl;
return 0;
}
3.赋值操作符重载
两种情况下需要对赋值操作符重载:
1.赋值号两边的表达式类型不一致(且无法进行转换)
2.需要进行“深拷贝”
一个浅拷贝的例子:
#include <iostream>
using namespace std;
class A
{
int num;
public:
A()
{
num = 0;
}
A(int i)
{
num = i;
}
void show()
{
cout<<num<<endl;
}
A& operator= (int i)
{
cout<<"you are in operator\n";
return *this; //这里只是一个简单的浅拷贝
}
};
int main()
{
A a;
a = 5; //赋值操作,调用赋值操作符函数,,不过只是钱拷贝
a.show(); //输出结果为 0,, 不是5
return 0;
}
一个深拷贝的例子:
#include <iostream>
#include <string>
using namespace std;
class Student
{
string name;
int age;
public:
Student()
{
name = "NULL";
}
Student(string s, int a)
{
name = s;
age = a;
}
//拷贝构造函数
Student(const Student& s)
{
*this = s; //在这里调用操作符函数 operator=
}
void show()
{
cout<<"The student's name is:"<<this->name<<endl;
cout<<"The student's age is:"<<this->age<<endl;
}
//实现的是深拷贝
Student& operator=(const Student& s)
{
name = s.name.substr(0, s.name.length());
age = s.age;
return *this;
}
};
int main()
{
Student s1("张三", 18);
Student s2("李四", 20);
s1.show();
Student s3 = s1; //这里调用拷贝构造函数,在拷贝函数里面再调用 操作符函数 operator=
s3.show();
Student s4;
s4 = s2; //这里直接调用操作符函数 operator=
s4.show();
return 0;
}
注意要点:
对赋值操作符进行重载是,通常将操作符函数的返回值定义为赋值左操作数类型的引用,这是为了实现表达式的求值,也是为了实现“链式操作”。
4.输入输出操作符重载
输入操作符是>>, 输出操作符是<<,又叫做流对象的“插入操作符”和“提取操作符”。其实这两个操作符最初是在C语言中用于整数的移位操作,到了C++中才利用操作符重载的技术将它们应用于输入、输出操作。
对于基本数据类型的数据类型的输入输出操作都已经在C++标准库中定义好,没有必要重新定义,也不允许重新定义。而对于用户自定义的类来说,如果想利用输入输出操作符进行本类对象的输入输出操作,就需要对>>和<<操作进行重载。
对于输出操作符<<进行重载,只能采用友元函数的形式进行,而不能讲operator<<() 声明为ostream类的成员函数,这是因为,ostream是在C++中标准类库中定义的类,既然是标准库,就不允许用户随意修改。
对于输入操作符>>进行重载,和输出操作符一样,也是只能采用友元函数的形式进行。
输出操作符函数原型: ostream & operator<<(ostream&, const className&) 这里只列出最常用也是最安全的一个
输入操作符函数原型: istream & operator>>(istream&, className&)
一个例子:
#include <iostream>
using namespace std;
class Complex
{
double real;
double image;
public:
Complex(double r = 0.0, double i = 0.0)
{
real = r;
image = i;
}
//operator<< 和 operator>> 只能是作为友元函数
//重载操作符<<
friend ostream& operator<<(ostream&, const Complex&);
//重载操作符>>
friend istream& operator>>(istream&, Complex&);
};
ostream& operator<<(ostream& out, const Complex& c)
{
out<<"in operator<<\n";
out<<c.real<<" + "<<c.image<<"i"<<endl;
return out; //千万别忘了返回 out
}
istream& operator>>(istream& in, Complex& c)
{
cout<<"in operator>>\n";
bool flag = false;
char ch;
while(!flag)
{
cout<<"please input a complex:";
in>>c.real;
in>>ch;
if(ch != '+')
continue;
in>>c.image;
in>>ch;
if(ch != 'i')
continue;
else
flag = true;
}
return in; //千万别忘了返回 in
}
int main()
{
Complex c;
cin>>c;
cout<<c;
return 0;
}
5.!操作符重载
“!”是一个一元操作符,它通常用于布尔量,表示逻辑取反。当在某个类中对 “!”操作符进行重载时,通常表明该类的对象出现了“异常”状况。 可以对”!“操作符进行重载,以表明用new 生成的对象是否构造成功。(new 操作在堆上申请空间)
对operator!进行重载可以有两种方式实现,既可以作为类的成员函数,也可以作为类 的友元函数。
具体情况看下面的例子:
#include <iostream>
using namespace std;
class A
{
int m;
char *str1;
char *str2;
public:
A(int n, int s1, int s2)
{
str1 = new char[s1];
str2 = new char[s2];
m = n;
}
~A()
{
delete str1;
delete str2;
}
void show()
{
cout<<m<<endl;
}
//重载 ! 操作符, 作为A的成员函数
//bool operator!()
//{
// cout<<"int operator!()\n";
// if(str1&&str2)
// return false;
// return true;
//}
friend bool operator!(const A& a);
};
bool operator!(const A& a)
{
cout<<"in frined operator!()\n";
if(a.str1 && a.str2)
return false;
return true;
}
int main()
{
A * p = new A(1, 2, 4);
if(!p) //这里只是普通的逻辑取反,并不调用重载函数operator!()
cout<<"!p\n";
if(!(*p)) //在这里调用重载函数operator!()
cout<<"!(*p)\n";
else
p->show();
delete p;
return 0;
}