1 操作符标记
单目操作符: - ++ -- * -> 等
双目操作符: - + > < += -= << >> 等
三木操作符: ? :
2 操作符函数
2.0 前言
C++编译器有能力把一个由操作数和操作符组成的表达式,
解释为对一个成员函数的调用, a + b --> a.operator+( b )
解释为对一个全员函数的调用。 a + b --> operator+( a,b )
该 全员函数 或 成员函数 被称为操作符函数。两个函数不要重复定义。
通过定义操作符函数,可以实现针对自定义类型的运算法则,并使之与基本类型一样参与各种表达式。
// complex_pre.cpp操作符函数
#include <iostream>
using namespace std;
class Human {
public:
Human( int age=0, const char* name="无名" ) : m_age(age),m_name(name) {
//【int m_age=age;】
//【string m_name(name);】
}
void getinfo( ) {
cout << "姓名: " << m_name << ", 年龄: " << m_age << endl;
}
Human sum( /* Human* this */ Human r ) {
return Human(this->m_age+r.m_age, (this->m_name+"+"+r.m_name).c_str() );
}
Human sub( /* Human* this */ Human r ) {
return Human(this->m_age-r.m_age, (this->m_name+"-"+r.m_name).c_str() );
}
private:
int m_age;
string m_name;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Human a(22,"张飞"), b(20,"赵云"), c(25,"关羽"), d(32,"马超");
Human res = a.sum(b); // a + b; ==> a.operator+(b) 或 operator+(a,b)
res.getinfo( );
res = c.sub(d); // c - d; ==> c.operator-(d) 或 operator-(c,d)
res.getinfo( );
return 0;
}
2.1 单目运算符表达式 #O / O#
成员函数形式: O.operator# ()
全局函数形式: operator# (O)
2.2 双目运算符表达式 L#R
成员函数形式: L.operator# (R) 左调右参:左操作数是调用对象,右操作数是参数对象
全局函数形式: operator# (L,R) 左一右二:左操作数是第一参数,右操作数是第二参数
2.3 三目运算符表达式 F#S#T
无法重载。
3 典型双目操作符
3.1 运算类: + - * / 等
左操作数可以为非常左值、常左值、右值
右操作数可以为非常左值、常左值、右值
表达式的结果为右值 (即,不是引用就行)
// double_operator1.cpp
#include <iostream>
using namespace std;
class Human { // 授权类(授予朋友权利的类)
public:
Human( int age=0, const char* name="无名" ) : m_age(age),m_name(name) {
//【int m_age=age;】
//【string m_name(name);】
}
void getinfo( ) {
cout << "姓名: " << m_name << ", 年龄: " << m_age << endl;
}
// 成员形式的操作符函数 万能指针是必要的 万能引用是必要的
// Human operator+( /* const Human* this */ const Human& that ) const {
// return Human(this->m_age+that.m_age, (this->m_name+"+"+that.m_name).c_str());
// }
private:
int m_age;
string m_name;
friend Human operator+( const Human& l, const Human& r ) ; // 友元声明
};
// 全局形式的操作符函数
Human operator+( const Human& l, const Human& r ) {
return Human(l.m_age+r.m_age, (l.m_name+"+"+r.m_name).c_str() );
}
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Human a(22,"张飞"), b(20,"赵云"); // 非常左值
const Human c(25,"关羽"), d(32,"马超"); // 常左值
Human res = a + b; // ==> a.operator+(b) 或 operator+(a,b)
res.getinfo( );
res = c + d; // ==> c.operator+(d) 或 operator+(c,d)
res.getinfo();
res= Human(45,"黄忠")+ Human(35,"刘备");//Human(45,"黄忠").operator+(Human(35,"刘备"))
//operator+( Human(45,"黄忠"), Human(35,"刘备"))
res.getinfo();
/*
int a=10, b=20; // 非常左值
const int c=30, d=40; // 常左值
|30| a + b;
a + c;
a + 5;
c + b;
5 + b;
*/
return 0;
}
3.2 赋值类 = += -= *= /= 等
左操作数必须为非常左值
右操作数可以为非常左值、常左值、右值
表达式结果为左操作数本身(而非副本)
operator=()就是拷贝赋值函数,编译器会默认给。
// double_operator2.cpp
#include <iostream>
using namespace std;
class Human { // 授权类(授予朋友权利的类)
public:
Human( int age=0, const char* name="无名" ) : m_age(age),m_name(name) {
//【int m_age=age;】
//【string m_name(name);】
}
void getinfo( ) {
cout << "姓名: " << m_name << ", 年龄: " << m_age << endl;
}
// 成员形式的操作符函数
Human& operator+=( /* Human* this */ const Human& that ) {
this->m_age = this->m_age + that.m_age;
this->m_name = this->m_name+"+"+that.m_name;
return *this;
}
private:
int m_age;
string m_name;
};
// 全局形式的操作符函数(自己课下写)
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Human a(22,"张飞"), b(20,"赵云"); // 非常左值
const Human c(25,"关羽"), d(32,"马超"); // 常左值
((a+=b)+=c)+=Human(45,"黄忠");
a.getinfo();
/*
a += b; // a.operator+=(b) 或 operator+=(a,b)
a.getinfo();
a += c; // a.operator+=(c) 或 operator+=(a,c)
a.getinfo();
a += Human(45,"黄忠"); // a.operator+=(Human(45,"黄忠"))
// 或 operator+=(a,Human(45,"黄忠"))
a.getinfo();
*/
/*
int a=10, b=20; // 非常左值
const int c=30, d=40; // 常左值
a = b;
a = c;
a = 5;
c = b; // error
5 = b; // error
*/
return 0;
}
3.3 比较类 > < == <= >= 等
左操作数为非常左值、常左值、右值
右操作数为非常左值、常左值、右值
表达式结果为bool
// double_operator3.cpp
#include <iostream>
using namespace std;
class Human { // 授权类(授予朋友权利的类)
public:
Human( int age=0, const char* name="无名" ) : m_age(age),m_name(name) {
//【int m_age=age;】
//【string m_name(name);】
}
void getinfo( ) {
cout << "姓名: " << m_name << ", 年龄: " << m_age << endl;
}
// 成员形式的操作符函数
bool operator==( /* const Human* this */ const Human& that ) const {
return this->m_age==that.m_age && this->m_name==that.m_name;
}
bool operator!=( /* const Human* this */ const Human& that ) const {
// return this->m_age!=that.m_age || this->m_name!=that.m_name;
return !(*this==that);
}
private:
int m_age;
string m_name;
};
// 全局形式的操作符函数(自己课下写)
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Human a(22,"张飞"), b(20,"赵云"); // 非常左值
const Human c(25,"关羽"), d(32,"马超"); // 常左值
cout << (a == b) << endl; // a.operator==(b) 或 ...
cout << (a != b) << endl; // a.operator!=(b) 或 ...
cout << (c == d) << endl; // c.operator==(d) 或 ...
cout << (c != d) << endl; // c.operator!=(d) 或 ...
cout << (Human(45,"黄忠")==Human(35,"刘备")) << endl;
// Human(45,"黄忠").operator==(Human(35,"刘备"))
cout << (Human(45,"黄忠")!=Human(35,"刘备")) << endl;
// Human(45,"黄忠").operator!=(Human(35,"刘备"))
/*
int a=10, b=20; // 非常左值
const int c=30, d=40; // 常左值
a == b;
a == c;
a == 5;
c == b;
5 == b;
*/
return 0;
}
4 典型单目操作符
4.1 运算类 - ~ ! 等
操作数为非常左值、常左值、右值
表达式的结果为右值
// single_operator1.cpp
#include <iostream>
using namespace std;
class Human { // 授权类(授予朋友权利的类)
public:
Human( int age=0, const char* name="无名" ) : m_age(age),m_name(name) {
//【int m_age=age;】
//【string m_name(name);】
}
void getinfo( ) {
cout << "姓名: " << m_name << ", 年龄: " << m_age << endl;
}
// 成员形式的操作符函数
Human operator-( /* const Human* this */ ) const {
return Human(-this->m_age, ("-"+this->m_name).c_str() );
}
private:
int m_age;
string m_name;
};
// 全局形式的操作符函数(自己课下写)
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Human a(22,"张飞"), b(20,"赵云"); // 非常左值
const Human c(25,"关羽"), d(32,"马超"); // 常左值
Human res = -a; // a.operator-() 或 ...
res.getinfo();
res = -c; // c.operator-() 或 ...
res.getinfo();
res = -Human(45,"黄忠"); // Human(45,"黄忠").operator-() 或 ...
res.getinfo();
/*
int a=10, b=20; // 非常左值
const int c=30, d=40; // 常左值
|-10| -a; // ok
|-30| -c; // ok
|-40| -40;// ok
*/
return 0;
}
4.2 前自增减类 ++O --O
操作数为非常左值
表达式结果为操作数本身(而非副本)
4.3 后自增减类 O++ O--
操作数为非常左值
表达式的结果为右值,且为自增减以前的值
// single_operator2.cpp
#include <iostream>
using namespace std;
class Human { // 授权类(授予朋友权利的类)
public:
Human( int age=0, const char* name="无名" ) : m_age(age),m_name(name) {
//【int m_age=age;】
//【string m_name(name);】
}
void getinfo( ) {
cout << "姓名: " << m_name << ", 年龄: " << m_age << endl;
}
// 成员形式的操作符函数
Human& operator++( /* Human* this */ ) {
this->m_age += 1; // 直接就加1
return *this;
}
Human operator++( /* Human* this */ int ) {
Human old = *this; // 克隆一份b原来的值
this->m_age += 1; // 直接就加1
return old; // 返回的为克隆的b原来的值
}
private:
int m_age;
string m_name;
};
// 全局形式的操作符函数(自己课下写)
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Human a(22,"张飞"), b(20,"赵云"); // 非常左值
const Human c(25,"关羽"), d(32,"马超"); // 常左值
(++a).getinfo(); // a.operator++() 或 operator++(a)
(/*|...|*/b++).getinfo(); // b.operator++(0) 或 operator++(b,0)
b.getinfo();
/*
int a=10, b=20; // 非常左值
const int c=30, d=40; // 常左值
++a; // ok
++c; // error
++5; // error
b++; // ok
c++; // error
5++; // error
*/
return 0;
}
5 其他操作符
5.1 输出流操作符 <<
左操作数(cout)为 非常左值 形式的输出流( ostream )对象
右操作数为 左值或右值
表达式的结果为左操作符本身(而非副本)
左操作数的类型为ostream,
(若以成员函数形式重载该操作符,就应将其定义为ostream类的成员),
但该类为标准库提供,无法添加新的成员,因此只能以全局函数形式重载该操作符:
ostream& operator<< ( ostream& os, const RTGHT& right ) { ... }
5.2 输入流操作符 >>
左操作数为 非常左值 形式的输入流( istream )对象
右操作数为 非常左值
表达式的结果为左操作符本身(而非副本)
左操作数的类型为istream,
(若以成员函数形式重载该操作符,就应将其定义为istream类的成员),
但该类为标准库提供,无法添加新的成员,因此只能以全局函数形式重载该操作符:
istream& operator>> ( istream& is, RIGHT& right ) { ... }
// io.cpp
#include <iostream>
using namespace std;
class Human {
public:
Human( int age=0, const char* name="无名" ) : m_age(age),m_name(name) {
//【int m_age=age;】
//【string m_name(name);】
}
void getinfo( ) {
cout << "姓名: " << m_name << ", 年龄: " << m_age << endl;
}
private:
int m_age;
string m_name;
friend ostream& operator<<( ostream& os, const Human& that ) ;
friend istream& operator>>( istream& is, Human& that ) ;
};
// 全局形式的操作符函数(自己课下写)
ostream& operator<<( ostream& os, const Human& that ) {
os << "姓名:" << that.m_name << ", 年龄:" << that.m_age;
return os;
}
istream& operator>>( istream& is, Human& that ) {
is >> that.m_name >> that.m_age;
return is;
}
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Human a(22,"张飞"), b(20,"赵云"); // 非常左值
const Human c(25,"关羽"), d(32,"马超"); // 常左值
// a.getinfo();
cout << a << endl; // operator<<(cout, a)
cout << c << endl; // operator<<(cout, c)
cout << Human(45,"黄忠") << endl; // operator<<(cout, Human(45,"黄忠") )
cin >> a; // operator>>(cin,a)
cout << a << endl;
/*
int a=10, b=20; // 非常左值
const int c=30, d=40; // 常左值
cout << a;
cout << c;
cout << 5;
*/
return 0;
}
5.3 下标操作符 []
一般用于在容器类型中以下标方式获取数据元素
非常容器的元素为非常左值,
常容器的元素为常左值。
// stack.cpp
// 简易的栈容器 -- 先进后出
#include <iostream>
using namespace std;
class Stack {
public:
Stack() : m_s(0) {
//【int arr[20];】
//【int m_s=0;】
}
void push(int data) { arr[m_s++] = data; } // 判满操作,自己课下写
int pop() { return arr[--m_s]; } // 判空操作,自己课下写
int size() { return m_s; }
const int& operator[]( /* const Stack* this */ size_t i) const { // 常函数
return this->arr[i];
}
int& operator[]( /* Stack* this */ size_t i) { // 非常函数
return this->arr[i];
}
private:
int arr[20]; // 保存数据
int m_s; // 保存数据个数
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Stack s; // 非常容器
for( int i=0; i<20; i++ ) {
s.push( 1000+i );
}
cout << "压栈后s容器中数据的个数:" << s.size() << endl;
s[5] = 888; // 非常容器的元素,就是非常左值 s.operator[](5)=888
for( int i=0; i<20; i++ ) {
cout << s[i] << ' ';
}
cout << endl;
cout << "读数据后s容器中数据的个数:" << s.size() << endl;
const Stack cs = s; // cs是常容器
cs[5] = 999; // cs.operator[](5)=999 应该让编译器报readonly错误
/*
int s[20] = {...}; // s是非常容器,s的元素就是非常左值
s[5] = 888;
const int cs[20] = {...}; // cs是常容器,cs的元素就是常左值
cs[5] = 999; // 报告readonly错误
*/
return 0;
}
5.4 类型转换操作符
1)若源类型是基本类型,目标类型是类类型,则
只能通过类型转换 构造 函数实现自定义类型转换:
class 目标类型 {
目标类型 ( const 源类型& src ) {...}
};
2)若源类型是类类型,目标类型是基本类型,则
只能通过类型转换 操作符 函数实现自定义类型转换:
class 源类型 {
operator 目标类型 (void) const {...}
};
3)若源类型和目标类型都是类类型,则
既优先通过类型转换 构造 函数,其次通过类型转换 操作符 函数实现自定义类型转换。
但两者没必要同时使用。
4)若源类型和目标类型都是基本类型,则
无法实现自定义类型转换,基本类型之间的转换规则是由编译器内置的隐式转换。
// cast1.cpp
// 类型转换构造函数 和 类型转换操作符函数
#include <iostream>
using namespace std;
class Integer {
public:
Integer(int i):m_i(i) {
//【int m_i=i;】
cout << "Integer类的类型转换构造函数被调用" << endl;
}
operator int( /* const Integer* this */ ) const {
cout << "Integer类的类型转换操作符函数被调用" << endl;
return this->m_i;
}
private:
int m_i;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
int m = 100;
// int-->Integer (基本类型-->类类型)
Integer ix = m; // 定义 匿名Integer类对象,利用 匿名Integer类对象.Integer(m)
->类型转换构造函数
// Integer ix = m.operator Integer()
-->int类中绝对没有一个operator Integer成员函数(走不通)
// Integer-->int (类类型-->基本类型)
int n = ix;
// 定义 匿名int类对象,利用 匿名int类对象.int(ix)
-->int类中绝对没有一个形参为Integer的构造函数(走不通)
// int n = ix.operator int() --> 类型转换操作符函数
return 0;
}
// cast2.cpp
// 类型转换构造函数/类型转换操作符函数 -- 指定 源类型 到 目标类型 的 转换规则
#include <iostream>
using namespace std;
class Dog; // 短式声明
class Cat {
public:
Cat( const char* name ) : m_name(name) {
//【string m_name(name);】
}
void talk( ) {
cout << m_name << ": 喵喵~~~" << endl;
}
operator Dog( /* const Cat* this */ ) const; // 声明
private:
string m_name;
friend class Dog; // 友元声明
};
class Dog {
public:
Dog( const char* name ) : m_name(name) {
//【string m_name(name);】
}
Dog( const Cat& c ) : m_name(c.m_name) { //类型转换构造函数(Cat-->Dog的转换规则)
//【string m_name=c.m_name;】
cout << "Dog类的类型转换构造函数被调用" << endl;
}
void talk( ) {
cout << m_name << ": 汪汪~~~" << endl;
}
private:
string m_name;
};
Cat::operator Dog( /* const Cat* this */ ) const { // 定义
cout << "Cat类的类型转换操作符函数被调用" << endl;
return Dog( this->m_name.c_str() );
}
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {
Cat smallwhite("小白");
// Cat-->Dog (类类型-->类类型)
Dog bigyellow = smallwhite; //定义 匿名Dog类对象,利用 匿名Dog类对象.Dog(smallwhite)
->类型转换构造函数
//Dog bigyellow = smallwhite.operator Dog()
->类型转换操作符函数
return 0;
}
6 操作符重载的局限
1)不是所有的操作符都能重载,以下操作符就不能重载:
作用域限定操作符 ::
直接成员访问操作符 .
条件操作符 ? :
字节长度操作符 sizeof
类型信息操作符 typeid
2)所有操作数均为基本类型的操作符也不能重载:
1 + 1 = 8 ?
7 友元
可以通过friend关键字,把
一个全局函数、另一个类的成员函数、另一个类整体,
声明为授权类的友元。
友元拥有访问授权类任何非公有成员的特权。(即访问所有成员)
友元声明可以出现在授权类的公有、私有、保护等任何区域,且不受 访问控制限定符 的约束。
友元不是成员,其作用域并不隶属于授权类,也不拥有授权类类型的this指针。
// twoDimensional_08.cpp 设计一个二维坐标系的 类
#include <iostream>
using namespace std;
class TwoDimensional {
public:
TwoDimensional( int x=0, int y=0 ) {
// 在this指向的内存空间中 定义m_x初值为随机数
// 在this指向的内存空间中 定义m_y初值为随机数
m_x = x;
m_y = y;
}
TwoDimensional operator+( /* const TwoDimensional* this */ const TwoDimensional& that ) const {
return TwoDimensional( this->m_x+that.m_x, this->m_y+that.m_y );
}
TwoDimensional& operator=( /* TwoDimensional* this */ const TwoDimensional& that ) {
this->m_x = that.m_x;
this->m_y = that.m_y;
return *this;
}
bool operator>( /* const TwoDimensional* this */ const TwoDimensional& that ) const {
return this->m_x*this->m_x+this->m_y*this->m_y > that.m_x*that.m_x + that.m_y*that.m_y;
}
TwoDimensional operator-( /* const TwoDimensional* this */ ) const {
return TwoDimensional( -this->m_x, -this->m_y );
}
TwoDimensional& operator++( /* TwoDimensional& this*/ ) {
++this->m_x;
++this->m_y;
return *this;
}
private:
int m_x; // 横坐标
int m_y; // 纵坐标
friend ostream& operator<<( ostream& os, const TwoDimensional& that );
};
ostream& operator<<( ostream& os, const TwoDimensional& that ) {
os << "横坐标:" << that.m_x << ", 纵坐标:" << that.m_y;
return os;
}
class ThreeDimensional {
public:
ThreeDimensional( int x=0, int y=0, int z=0 ) : m_x(x),m_y(y),m_z(z) {}
operator TwoDimensional( /* const ThreeDimensional* this */ ) const {
return TwoDimensional( this->m_x, this->m_y );
}
private:
int m_x;
int m_y;
int m_z;
};
int main( void ) {
TwoDimensional a(1,2), b(3,4);
cout << "a-->" << a << ", b-->" << b << endl; // cout 打印 坐标对象
TwoDimensional res = a + b; // 坐标间求和
cout << res << endl;
a = b; // 坐标间赋值
cout << a << endl;
cout << (a > b) << endl; // 坐标间比较大小
res = -a; // 坐标取反
cout << res << endl;
cout << ++a << endl; // 坐标自增
ThreeDimensional c(100,200,300);
TwoDimensional d = c; // TwoDimensional d = c.operator TwoDimensional();
cout << d << endl;
return 0;
}