C++从入门到放弃
操作符重载
- 基本概念
操作符重载就是一些具有特殊名字的函数,operator操作符
,可以实现将已经定义的操作符重新定义,完成对自定义类型对象的运算功能 - 双目操作符
L#R
操作符重载注意事项
- 如果一个操作符的所有操作数都是基本类型则无法重载
- 操作符重载不会给变编译器预定义的优先级
- 操作符重载不能改变操作数的个数
- 无法通过操作符重载机制发明新的符号
- 只能使用成员函数形式的操作符
=
[]
( )
->
不可以重载的操作符
- 作用域限定操作符
::
- 直接成员访问操作符
.
- 直接成员指针解引用操作符
.*
- 条件操作符
a>b?a:b
- 字节长度操作符
sizeof
- 类型信息操作符
typeid
双目操作符
计算类双目操作符+
-
*
/
- 返回值类型
表达式结果是右值,不能对表达式结果再次赋值,返回值因此是常对象 - 参数
右操作数既可以是左值也可以是右值,因此使用常引用
左操作数既可以是左值也可以是右值,因此使用常函数(修饰this指针) - 实现
成员函数形式(左调右参) 常函数
L#R
可以被编译器处理成L.operator#(R)
的成员函数调用形式,该函数的返回值就是表达式的结果
全局函数形式(左右都参) 友元函数
L#R
可以被编译器处理成operator#(L,R)
的全局函数调用形式,该函数的返回值就是表达式结果 - 注意
两种形式只能使用一个
如果使用全局函数形式,在访问类中私有成员时需要使用friend
关键字在类中的私有成员将其声明为该类的友元,友元函数可以访问该类的所有成员
#include <iostream>
using namespace std;
class Complex {
public:
Complex(int r, int i) : m_r(r), m_i(i) {}
void print() const {
cout << m_r << "+" << m_i << "i" << endl;
}
//成员函数调用形式
const Complex operator+(const Complex &other) const {
Complex tmp(this->m_r + other.m_r, this->m_i + other.m_i);
return tmp;
}
private:
int m_r, m_i;
friend const Complex operator-(const Complex &l, const Complex &r);
};
//全局函数调用形式,在类中声明成友元函数
const Complex operator-(const Complex &l, const Complex &r) {
Complex tmp((l.m_r - r.m_r), (l.m_i - r.m_i));
return tmp;
}
int main() {
Complex a1(3, 7);
Complex a2(2, 3);
a1.print();//3+7i
a2.print();//2+3i
//等同于Complex a3 = a1.operator+(a2);成员函数调用形式
Complex a3 = a1 + a2;
a3.print();//5+10i
//等同于Complex a4 = operator-(a1,a2);全局函数调用形式
Complex a4 = a1 - a2;
a4.print();//1+4i
return 0;
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
3+7i
2+3i
5+10i
1+4i
进程已结束,退出代码 0
赋值类双目操作符+=
-=
...
- 返回值类型
表达式结果是左值并且是左操作数自身,表达式结果可以再次赋值;因此返回的是非常对象引用
- 参数
左操作数必须是左值,而右操作数既可以是左值也可以是右值;因此左参数是非常引用,右参数是常引用
- 实现
成员函数形式(左调右参) 非常函数
L#R
可以被编译器处理成L.operator#(R)
的成员函数调用形式,该函数的返回值就是左操作数本身return *this;
全局函数形式(左右都参) 非常函数
L#R
可以被编译器处理成operator(L.R)
的全局函数调用形式,该函数的返回值就是左操作数本身return L;
- 注意
两种形式只能使用一个
如果使用全局函数形式,在访问类中私有成员时,需要使用friend
关键字在类中的私有成员里,将其声明为友元,友元函数可以访问该类的所有成员
#include <iostream>
using namespace std;
class Complex {
public:
Complex(int r, int i) : m_r(r), m_i(i) {}
void print() const {
cout << m_r << "+" << m_i << "i" << endl;
}
//成员函数调用形式
Complex &operator+=(const Complex &r) {
this->m_r += r.m_r;
this->m_i += r.m_i;
return *this;
}
private:
int m_r, m_i;
friend Complex &operator-=(Complex &l, const Complex &r);
};
//全局函数调用形式,在类中声明成友元函数
Complex &operator-=(Complex &l, const Complex &r) {
l.m_r -= r.m_r;
l.m_i -= r.m_i;
return l;
}
int main() {
Complex a1(3, 7);
Complex a2(2, 3);
a1.print();//3+7i
a2.print();//2+3i
//等同于Complex a2 = a2.operator+=(a1);成员函数调用形式
a2 += a1;
a2.print();//5+10i
Complex a3(7, 9);
(a1 += a2) = a3;
a1.print();//7+9i
//等同于Complex a1 = operator-=(a1,a2);
a1 -= a2;
a1.print();//2+-1i
(a3 -= a1) = a2;
a3.print();//5+10i
return 0;
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
3+7i
2+3i
5+10i
7+9i
2+-1i
5+10i
进程已结束,退出代码 0
单目操作符重载 #O
计算类单目操作符 -
~
...
- 返回值类型
表达式结果是是右值,不能对表达式结果再次赋值;因此返回的对象时常对象 - 参数
操作数自身可以是左值.也可以是右值;因此参数是常引用 - 实现
成员函数 常函数
#O
单目操作符重载可以被编译器处理成O.operator#()
的成员函数调用形式,该函数的返回值就是表达式的结果,是一个临时变量
全局函数形式 常函数
#O
可以被编译器处理成operator#(O)
的全局函数调用形式,该函数的返回值就是表达式的结果,是一个临时对象 - 注意
两种形式只能使用一个
如果使用全局函数形式,在访问类中私有成员时,需要使用friend
关键字在类中的私有成员;里,将其声明为该类友元,友元函数可以访问该类的所有成员
#include <iostream>
using namespace std;
class Integer {
public:
explicit Integer(int i) : m_i(i) {}
void print() const {
cout << m_i << endl;
}
//成员函数调用
const Integer operator-(void) const {
Integer tmp(-m_i);
return tmp;
}
private:
int m_i;
//全局函数调用
friend const Integer operator~(const Integer &l);
};
//全局函数调用
const Integer operator~(const Integer &l) {
Integer tmp(l.m_i * l.m_i);
return tmp;
}
int main() {
Integer i(100);
i.print();//100
Integer j(-i);
j.print();//-100
j = (~i);
j.print();//10000
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
100
-100
10000
进程已结束,退出代码 0
自增减类单目操作符++
--
++i
--i
- 返回值类型
表达式结果是左值,而且返回的是就是操作数自身,因此返回值类型是左值引用 - 参数
操作数必须是左值,因此参数必须是左值引用 - 实现
成员函数形式为非常函数
#O
可以被编译器处理成O.operator#()
的成员函数调用形式,该函数的返回值就是操作数本身
全局函数形式为非常函数
可以被编译器处理成operator#(O)
的全局函数调用形式,该函数的返回值就是操作符本身
i++
i--
- 返回值类型
表达式结果是右值,右值,并且是操作数之前的数值,是一个临时变量 - 参数
操作数必须是左值 - 实现
成员函数形式O#(int)
可以被编译器处理成O.operator#(int/*哑元参数*/)
的成员函数调用形式,该函数的返回值就是一个临时变量
全局函数形式#(O,int)
可以被编译器处理成operator#(O,int/*哑元参数*/)
的成员函数调用形式,该函数的返回值就是一个临时变量
#include <iostream>
using namespace std;
class Integer {
public:
explicit Integer(int i) : m_i(i) {}
void print() const {
cout << m_i << endl;
}
//成员函数调用a++
const Integer operator++(int/*哑元*/) {
Integer tmp = *this;
++m_i;
return tmp;
}
//成员函数调用++a;
Integer &operator++() {
++m_i;
return *this;
}
private:
int m_i;
//全局函数调用a++
friend const Integer operator--(Integer &l, int/*哑元*/);
//全局函数调用--a
friend Integer &operator--(Integer &l);
};
//全局函数调用a++
const Integer operator--(Integer &l, int/*哑元*/) {
Integer tmp = l;
--l.m_i;
return tmp;
}
//全局函数调用--a
Integer &operator--(Integer &l) {
--l.m_i;
return l;
}
int main() {
Integer i(100);
i.print();//100
Integer j = i++;
j.print();//100
i.print();//101
j = i--;
j.print();//101
i.print();//100
j = ++i;
j.print();//101
i.print();//101
j = --i;
j.print();//100
i.print();//100
j = ++ ++i;
j.print();//102
i.print();//102
j = -- --i;
j.print();//100
i.print();//100
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
100
100
101
101
100
101
101
100
100
102
102
100
100
进程已结束,退出代码 0
输入输出操作符 <<
>>
-
功能
实现自定义类型对象的直接输出或输入 -
形式
成员函数调用形式operator>>(O);
全局函数调用形式operator<<(cout,O);
-
注意
cout
cin
对应的标准输出和输入流类是C++标准库定义好的,要想添加新的成员函数很麻烦,所以使用全局成员函数的形式 -
全局函数调用实现
//将r中的数据读取到输出缓冲区中,r可以是左值也可以是右值
friend ostream& operator<<(ostream& os,const Right& r){
return os;
}
//把输入缓冲区的内容读入到参数r中,r只能是左值
friend istream& operator>>(istream& is,Right& r){
return is;
}
- 代码
#include <iostream>
using namespace std;
class Complex {
public:
Complex(int r, int i) : m_r(r), m_i(i) {}
private:
int m_r, m_i;
friend ostream &operator<<(ostream &os, const Complex &r);
friend istream &operator>>(istream &is, Complex &a);
};
ostream &operator<<(ostream &os, const Complex &r) {
os << r.m_r << "+" << r.m_i << "i" << endl;
return os;
}
istream &operator>>(istream &is, Complex &a) {
int r, i;
is >> m_r >> m_i;
return is;
}
//全局函数调用形式,在类中声明成友元函数
int main() {
Complex a1(3, 7);
Complex a2(2, 3);
cout << a1 << endl;
cin >> a2;
cout << a1 << "," << a2 << endl;
return 0;
}
#include <iostream>
using namespace std;
class T {
public:
T() {
for (int i = 0, count = 1; i < 3; i++) {
for (int j = 0; j < 3; j++) {
m_t[i][j] = count++;
}
}
}
const T operator+(const T &other) const {
T tmp;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
tmp.m_t[i][j] = this->m_t[i][j] + other.m_t[i][j];
}
}
return tmp;
}
private:
int m_t[3][3];
friend ostream &operator<<(ostream &os, const T &t);
};
ostream &operator<<(ostream &os, const T &t) {
for(int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << t.m_t[i][j] << " ";
}
cout << endl;
}
return os;
}
//全局函数调用形式,在类中声明成友元函数
int main() {
T t1;
T t2;
T t3 = t1 + t2;
cout << t3;
return 0;
}
/* 1 2 3
* 1 a b c
* 2 a b c
* 3 a b c
*/
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
2 4 6
8 10 12
14 16 18
进程已结束,退出代码 0
下标访问操作符 [ ]
- 功能实现让自定义类型的对象能够像数组一样去使用
- 注意非常对象返回左值,常对象返回右值
- 实现
#include <iostream>
using namespace std;
class T {
public:
T(std::size_t size) {
m_arr = new int[size];
}
~T() {
delete[] m_arr;
}
int &operator[](std::size_t i) {
return m_arr[i];
}
const int &operator[](std::size_t i) const {
// return m_arr[i];
return const_cast<T&>(*this)[i];//复用代码
}
private:
int *m_arr;
};
int main() {
T t1(10);
t1[1] = 1;
t1[3] = 2;
cout << t1[1] << t1[3];
/* const T t2(3);//重载常函数版本,报错
t2[1] = 111;
t2[3] = 222;
cout << t2[1] << t2[3];*/
/*
/home/panda/WorkSpace/memptr/main.cpp: In function ‘int main()’:
/home/panda/WorkSpace/memptr/main.cpp:33:11: error: assignment of read-only location ‘t2.T::operator[](1)’
33 | t2[1] = 111;
/home/panda/WorkSpace/memptr/main.cpp:34:11: error: assignment of read-only location ‘t2.T::operator[](3)’
34 | t2[3] = 222;
*/
return 0;
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
12
进程已结束,退出代码 0
函数操作符()
- 功能实现让自定义类型的对象能够像函数一样去使用,这样的对象被称为
仿函数
- 注意对于返回结果,参数个数,参数类型没有任何限制
#include <iostream>
using namespace std;
class Func {
public:
int operator()(int i, int j) {
return i * j;
}
int operator()(int i) {
return i * i;
}
private:
};
int main() {
Func f;
cout << f(3, 2)<<endl;
cout << f(4);
return 0;
}
new
delete
操作符
- 对象动态创建和销毁过程
- 调用
operator new
分配对象所需要的内存编译器实现的operator new - 调用类中的构造函数
- 调用类的析构函数
- 调用
operator delete
编译器实现的operator delete
- 注意
在对象创建,内存分配之前还没有对象可以使用,所以要是用静态成员对象 - 形式
static void* operator new(size_t size){...}
static void operator delete(void* pv){...}
#include <iostream>
using namespace std;
class A {
public:
A(){
cout<<"create A"<<endl;
}
~A(){
cout <<"delete A"<<endl;
}
static void* operator new(size_t size){
cout<<"malloc A"<<endl;
void* pv = malloc(size);
}
static void operator delete (void* pv){
cout <<"free A"<<endl;
free(pv);
}
private:
};
int main() {
//1>先分配内存
// A* pa = A*A::operator new(sizeof(A));
//2>在调用构造函数
// pa->构造函数
A* pa =new A;
delete pa;
return 0;
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
malloc A
create A
delete A
free A
进程已结束,退出代码 0
操作符重载速查表
双目操作符 | 表达式结果 | 右操作数 | 左操作数(函数体影响左操作数的this 指针) |
---|---|---|---|
计算类双目操作符 | 右值 | 可左可右 | 可左可右 |
成员函数调用 | 返回值类型const | 常引用 | 常函数 |
全局函数调用 | 返回值类型const | 常引用 | 常引用 |
– | – | – | – |
赋值类双目操作符 | 左值,并且是左操作数自身 | 可左可右 | 必须左值 |
成员函数调用 | cosnt 修饰return *this; | 常引用 | 普通函数 |
全局函数调用 | cosnt 修饰return 左操作数; | 常引用 | 左值引用 |
单目操作符 | 表达式结果 | 操作数 |
---|---|---|
计算类双目操作符 | 右值 | 可左可右 |
成员函数调用 | cosnt 修饰 | 常函数 |
全局函数调用 | cosnt 修饰 | 常引用 |
– | – | – |
自增自减单目操作符 | - | - |
a++; a--; /*哑元参数int*/ | 右值,并且是操作数之前的数值 | 必须是左值 |
成员函数调用 | cosnt 修饰返回值return中间变量; | 普通函数 |
全局函数调用 | cosnt 修饰返回值return中间变量; | 左值引用 |
++a; --a; | 左值,并且是左操作数自身 | 必须是左值 |
成员函数调用 | 加引用,return *this; | |
普通函数 | ||
全局函数调用 | 加引用,return 操作数; | 左值引用 |