目录
概念
类内重载操作符
类外重载操作符
使用注意
对象类型转换
封装Iterator
概念
C++提供的运算符,通常只支持对于基本数据类型和标准库中提供的类进行操作,对于自定义类型如果想通过操作符实现对应的操作,需要自定义重载的操作符并实现具体的功能。
C++编译器,当遇到该运算符时就调用此函数来行使运算符功能,本质是通过函数扩展了操作符的功能。重载操作符函数需要使用关键字operator作为函数名的一部分,重载操作符一般要有返回值,方便继续和其他操作符去操作。
可分两类:类内重载、类外重载
类内重载操作符
在类内重载,作为类成员函数,需要用对象调用,使用场景需要根据函数的参数一致(包括类型和顺序),注意在类内重载的操作符函数有隐藏的this指针作为第一个参数。
在使用时要注意重载操作符的参数类型和顺序,可以直接使用操作符,也可以显示通过对象调用重载的操作符。
class CTest {
public:int m_a;CTest (/*int m_a*/) :m_a(1) {//this->m_a = m_a;}int add(int a) {return this->m_a + a;}//函数名 operator加上要重载的符号,参数要与操作符的使用规则和使用场景一致(参数的顺序,类型匹配),//返回类型一般是要存在的,目的是为了和后续的操作符继续去操作,int operator+(/*CTest * const this*/int a) {return this->m_a + a;}int operator+=(int a) {return m_a += a;}};
int main() {CTest tst;//tst + 2; //tst.m_a + 2cout << tst.add(2) << endl;cout << tst + 2 << endl;//2 + tst; //不能匹配 int operator+(/*CTest * const this*/int a)int b = tst + 2; //可以承接返回类型cout << tst.operator+(3) << endl;//通过对象显示调用这个重载操作符函数tst += 4;cout << tst.m_a << endl;return 0;
}
对于单目运算符++,有左++和右++两种,为了区分右++,我们需要额外指定一个int类型的参数,这个参数只是用来区分,并无实际意义。
class CTest{//左++int operator++() {return ++this->m_a;}//右++int operator++(int) {return this->m_a++;}
};
类外重载操作符
在类外重载操作符时,至少需要包含一个自定义类型,在类内的重载操作符函数有默认的this且为自定义类型,所以定义函数时忽略了第一个参数,但类外重载没有隐藏的参数了,一般比类内要多一个参数。但要注意是否与类内重载的函数有冲突。
int operator+(int a, CTest& tst) {return a + tst.m_a;
}int operator+(CTest& tst,int a) {return a + tst.m_a;
}int operator*(CTest& tst) {return tst.m_a;
}//左++
int operator++(CTest & tst) {return ++tst.m_a;
}//右++
int operator++(CTest& tst,int) {return tst.m_a++;
}
如果类内类外产生歧义,调用不明确,可以用显示的方式来调用,但是不推荐。
cout << 2 + tst << endl; //3//cout << tst + 3<< endl; //4cout << tst.operator+(4) << endl;//5
自定义重载输入输出操作符,一般在类外重载。
ostream& operator<<(ostream& os, CTest& tst) {os << tst.m_a;return os;
}istream& operator>>(istream& is, CTest& tst) {is >> tst.m_a;return is;
}
使用注意
对于同一个操作符来说,写在不同的位置代表不同的含义,*p和a*b,那么重载这个操作符需要注意参数的数量、顺序不同代表不同的含义。
//类内重载
int operator*(); //间接引用
int operator*(int); //乘法
注意:
- 不能重载的运算符:长度运算符 sizeof、三目运算符 ? :、成员选择符 . 、作用域运算符 :: 等。
- 还有一些操作符只能在类内重载,赋值=,下标[],调用(),成员指向->,操作符必须被定义为类成员操作符。
- 重载操作符不能改变操作符的用法,原来有几个操作数、操作数在左边还是右边,这些都不会改变。
- 运算符重载函数不能有默认的参数,否则就改变了运算符操作数的个数,这显然是错误的。
- 重载操作符不能改变运算符的优先级和结合性。
- 不能创建新的运算符。
对象类型转换
上面重载等号操作符operator=,能让其他的类型赋值到当前类对象中,但是如果反过来写则会报错,类型不匹配,因为operator=只能在类内重载。
此时可以重载某个类型,这样定义该类对象就可以像这个类型一样去使用。
函数格式为:
operator type(){return type_value; //类型要和type一致
}
函数在写法上无参数,无返回值,但函数体中应该有return,且return的变量类型要和重载的类型一致。
operator int(){int a = 10;return a;
}
我们通常会将指针类型转换为bool类型用以判断
class CTest {
public:char* m_p;CTest() :m_p(new char[] {"123"}) {}~CTest() {if (m_p) delete []m_p;m_p = nullptr;}operator bool() {return m_p;}};
void fun(CTest& tst) {if (tst) cout << tst.m_p << endl;
}
如果同时存在重载操作符和重载类型,那么优先匹配重载的操作符
int a = tst + 10; //operator+
a = 10 + tst; //operator int
a = tst; //operator int
当然也可以显示的调用 类型转换函数
int a = tst.operator int() + 10; //operator int
封装Iterator
在学过重载操作符后,我们可以封装链表的迭代器,包含临时指针和对应的操作。
class CIterator {
private:Node* m_pNode;
public:CIterator():m_pNode(nullptr){}CIterator(Node* pNode) :m_pNode(pNode) {}Node* operator=(Node* pNode) {m_pNode = pNode;return m_pNode;}bool operator!=(Node* pNode) {return m_pNode != pNode;}bool operator==(Node* pNode) {return m_pNode == pNode;}operator bool() {return m_pNode;}int operator*() {return m_pNode->val;}//左++Node* operator++() {m_pNode = m_pNode->pNext;return m_pNode;}Node* operator++(int) {Node* pTemp = m_pNode; //先标记一下m_pNode = m_pNode->pNext; //后去移动return pTemp;}};
使用迭代器遍历链表
void ShowList() {//迭代器CIterator ite(m_pHead); while (ite != nullptr) { cout << *ite << " ";++ite;}cout << endl;}