重载运算符的规则如下:
① C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载
② C++不能重载的运算符只有5个。
③ 重载不能改变运算符运算对象的个数
④ 重载不能改变运算符的优先级和结合性
⑤ 重载运算符的函数不能有默认的参数
⑥ 重载的运算符必须和用户定义的自定义类型的对象一起使用,至少应有一个是类对象,即不允许参数全部是C++的标准类型。
⑦ 一般情况下,单目运算符最好重载为类的成员函数,双目运算符最好重载为类的友元函数。
⑧ 双目运算符=、()、[]、->不能重载为类的友元函数
⑨ 类型转换函数只能定义为一个类的成员函数,而不能定义为类的友元函数;
⑩ 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好;
11.若运算符所要的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数
12.当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是对该类对象的引用)如果左边的操作数必须是一个不同类的对象,或者是一个内部类型的对象,该运算符函数必须作为一个友元函数来实现。
数组类封装
MyArray.h
#pragma once
#include <iostream>
using namespace std;
class MyArray
{
public:
MyArray(); //默认构造 默认100容量
MyArray(int capacity);
MyArray(const MyArray& array);
~MyArray();
//尾插法
void push_Back(int val);
//根据索引获取值
int getData(int index);
//根据索引设置值
void setData(int index, int val);
//获取数组大小
int getSize();
//获取数组容量
int getCapacity();
//[]运算符重载
int& operator[](int index );
private:
int * pAddress; //指向真正存储数据的指针
int m_Size; //数组大小
int m_Capacity; //数组容量
};
MyArray.cpp
#include "MyArray.h"
//默认构造
MyArray::MyArray()
{
this->m_Capacity = 100;
this->m_Size = 0;
this->pAddress = new int[this->m_Capacity];
}
//有参构造 参数 数组容量
MyArray::MyArray(int capacity)
{
//cout << "有参函数调用" << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddress = new int[this->m_Capacity];
}
//拷贝构造
MyArray::MyArray(const MyArray& array)
{
cout << "拷贝构造调用" << endl;
this->pAddress = new int[array.m_Capacity];
this->m_Size = array.m_Size;
this->m_Capacity = array.m_Capacity;
for (int i = 0; i < array.m_Size;i++)
{
this->pAddress[i] = array.pAddress[i];
}
}
//析构
MyArray::~MyArray()
{
if (this->pAddress != NULL)
{
//cout << "析构调用" << endl;
delete[] this->pAddress;
this->pAddress = NULL;
}
}
void MyArray::push_Back(int val)
{
//判断越界? 用户自己处理
this->pAddress[this->m_Size] = val;
this->m_Size++;
}
int MyArray::getData(int index)
{
return this->pAddress[index];
}
void MyArray::setData(int index, int val)
{
this->pAddress[index] = val;
}
int MyArray::getSize()
{
return this->m_Size;
}
int MyArray::getCapacity()
{
return this->m_Capacity;
}
//[]重载实现
int& MyArray::operator[](int index)
{
return this->pAddress[index];
}
数组类的封装.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include "MyArray.h"
using namespace std;
void test01()
{
//堆区创建数组
MyArray * array = new MyArray(30);
MyArray * array2 = new MyArray(*array); //new方式指定调用拷贝构造
MyArray array3 = *array; //构造函数返回的本体
//MyArray * array4 = array; //这个是声明一个指针 和array执行的地址相同,所以不会调用拷贝构造
delete array;
//尾插法测试
for (int i = 0; i < 10;i++)
{
array2->push_Back(i);
}
//获取数据测试
for (int i = 0; i < 10;i++)
{
cout << array2->getData(i) << endl;
}
//设置值测试
array2->setData(0, 1000);
cout << array2->getData(0) << endl;;
//获取数组大小
cout << "array2 的数组大小为: " << array2->getSize() << endl;
//获取数组容量
cout << "array2 的数组容量为: " << array2->getCapacity() << endl;
//获取 设置 数组内容 如何用[]进行设置和访问
array3.push_Back(100000);
cout << array3.getData(0) << endl;
cout << array3[0] << endl;
array3[0] = 100; // 100000 = 100
cout << array3[0] << endl;
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
加号运算符重载
定义重载的运算符就像定义函数,只是该函数的名字是operator@,这里的@代表了被重载的运算符。函数的参数中参数个数取决于两个因素。
运算符是一元(一个参数)的还是二元(两个参数);
运算符被定义为全局函数(对于一元是一个参数,对于二元是两个参数)还是成员函数(对于一元没有参数,对于二元是一个参数-此时该类的对象用作左耳参数)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person
{
public:
Person(){};
Person(int a, int b) :m_A(a), m_B(b)
{}
//+号运算符重载 成员函数 二元
/*Person operator+ ( Person & p)
{
Person tmp;
tmp.m_A = this->m_A + p.m_A;
tmp.m_B = this->m_B + p.m_B;
return tmp;
}*/
int m_A;
int m_B;
};
//利用全局函数 进行+号运算符的重载
Person operator+ ( Person &p1,Person& p2) //二元 p1 + p2
{
Person tmp;
tmp.m_A = p1.m_A + p2.m_A;
tmp.m_B = p1.m_B + p2.m_B;
return tmp;
}
Person operator+ (Person &p1, int a) //二元
{
Person tmp;
tmp.m_A = p1.m_A + a;
tmp.m_B = p1.m_B + a;
return tmp;
}
void test01()
{
Person p1(10, 10);
Person p2(10, 10);
Person p3 = p1 + p2; // p1 + p2 从什么表达式转变的? 成员函数:p1.operator+(p2) 全局函数:operator+(p1,p2)
Person p4 = p1 + 10; //重载的版本
cout << "p3 的 m_A: " << p3.m_A << " m_B: " << p3.m_B << endl;
//operator+(p1, p2);
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
对于内置的数据类型的表达式的运算符是不可能改变的(例如想重载int类型的数据+号)
左移运算符重载
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person
{
friend ostream& operator<<(ostream &cout, Person & p1);
public:
Person(){}
Person(int a, int b)
{
this->m_A = a;
this->m_B = b;
}
/*void operator<<() 重载左移运算符不可以写到成员函数中
{
}*/
private:
int m_A;
int m_B;
};
ostream& operator<<(ostream &cout , Person & p1 ) //第一个参数 cout 第二个参数 p1 (返回类型需要加上&,否则报错)要保证全局只有一个cout
{
cout << "m_A = " << p1.m_A << " m_B = " << p1.m_B;
return cout;
}
void test01()
{
Person p1(10, 10);
cout << p1 << "helloworld" <<endl;
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
前置后置的递增递减运算符重载需要反复看
前置后置的递增递减运算符重载
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger & myInt);
public:
MyInteger()
{
m_Num = 0;
};
//前置++重载
MyInteger& operator++()
{
this->m_Num++;
return *this;
}
//后置++ 重载
MyInteger operator++(int)
{
//先保存目前数据
MyInteger tmp = *this;
m_Num++;
return tmp;
}
int m_Num;
};
ostream& operator<<( ostream& cout ,MyInteger & myInt)
{
cout << myInt.m_Num;
return cout;
}
void test01()
{
MyInteger myInt;
// 前置++
cout << ++(++myInt) << endl;
cout << myInt << endl;
//cout << myInt++ << endl; // 后置++
//cout << myInt << endl;
}
int main(){
test01();
/*int a = 10;
cout << ++(++a) << endl;
cout << a << endl;*/
system("pause");
return EXIT_SUCCESS;
}
#include<iostream>
using namespace std;
int main(){
int a=10;
int b=10;
cout<<"a:"<<a++<<" "<<"b:"<<++b;
}
指针运算符重载
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this->m_Age = age;
}
void showAge()
{
cout << "年龄为:" << this->m_Age << endl;
}
~Person()
{
cout << "Person的析构调用" << endl;
}
int m_Age;
};
//智能指针
//用来托管自定义类型的对象,让对象进行自动的释放
class smartPointer
{
public:
smartPointer(Person * person)
{
this->person = person;
}
//重载->让智能指针对象 想Person *p一样去使用
Person * operator->()
{
return this->person;
}
//重载 *
Person& operator*()
{
return *this->person;
}
~smartPointer()
{
cout << "智能指针析构了" << endl;
if (this->person !=NULL)
{
delete this->person;
this->person = NULL;
}
}
private:
Person * person;
};
void test01()
{
//Person p1(10); //自动析构
//Person * p1 = new Person(10);
//p1->showAge();
// delete p1;
smartPointer sp(new Person(10)); //sp开辟到了栈上,自动释放
sp->showAge(); // sp->->showAge(); 编译器优化了 写法
(*sp).showAge();
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
赋值运算符重载
系统默认给类提供赋值运算符写法,是简单的值拷贝,导致如果类中有指向堆区的指针,就可能出现深浅拷贝的问题,所以要重载=运算符。如果想链式编程 return *this
返回值是引用:
返回的是对象本身,可以继续对它进行赋值等操作。
返回值非引用:
返回的是对象的一个值。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//一个类默认创建 默认构造、析构、拷贝构造 operator=赋值运算符 进行简单的值传递
class Person
{
public:
Person(int a)
{
this->m_A = a;
}
int m_A;
};
void test01()
{
Person p1(10);
Person p2(0);
p2 = p1; //赋值
cout << "p2 的m_A" << p2.m_A <<endl;
}
class Person2
{
public:
Person2(char * name)
{
this->pName = new char[strlen(name) + 1];
strcpy(this->pName, name);
}
//重载 = 赋值运算符
Person2& operator= ( const Person2 & p)
{
//判断如果原来已经堆区有内容,先释放
if (this->pName != NULL)
{
delete[] this->pName;
this->pName = NULL;
}
this->pName = new char[strlen(p.pName) + 1];
strcpy(this->pName, p.pName);
return *this;
}
~Person2()
{
if (this->pName != NULL)
{
delete[] this->pName;
this->pName = NULL;
}
}
char * pName;
};
void test02()
{
Person2 p1("狗蛋");
Person2 p2("狗剩");
Person2 p3("");
p3 = p2 = p1;
cout << p2.pName << endl;
cout << p3.pName << endl;
//int a = 10;
//int b = 20;
//int c;
//c = a = b; //都是20
//cout << a << " " << b << " " << c << endl;
}
int main(){
//test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
关系运算符重载
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
using namespace std;
// ==
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
bool operator==( Person & p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
bool operator!=( Person & p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return false;
}
return true;
}
public:
string m_Name;
int m_Age;
};
void test01()
{
Person p1("小明", 10);
Person p2("小强", 15);
Person p3("小强", 15);
if ( p1 == p2)
{
cout << "p1 和 p2 相等" << endl;
}
else
{
cout << "p1 和 p2 不相等" << endl;
}
if (p2 == p3)
{
cout << "p2 和 p3 相等" << endl;
}
else
{
cout << "p2 和 p3 不相等" << endl;
}
if (p1 != p2)
{
cout << "p1 和 p2 不相等" << endl;
}
else
{
cout << "p1 和 p2 相等" << endl;
}
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
函数调用运算符重载
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
using namespace std;
// ()重载
class MyPrint
{
public:
void operator()( string text)
{
cout << text << endl;
}
};
void test01()
{
MyPrint myPrint;
myPrint("hello world1111"); // 仿函数
}
class MyAdd
{
public:
int operator()(int v1,int v2)
{
return v1 + v2;
}
};
void test02()
{
cout << MyAdd()(1, 1) << endl; //匿名对象
}
int main(){
test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
不要重载&&,||
不能重载operator&& 和 operator|| 的原因是,无法在这两种情况下实现内置操作符的完整语义。说得更具体一些,内置版本版本特殊之处在于:内置版本的&&和||首先计算左边的表达式,如果这完全能够决定结果,就无需计算右边的表达式了–而且能够保证不需要。我们都已经习惯这种方便的特性了。
我们说操作符重载其实是另一种形式的函数调用而已,对于函数调用总是在函数执行之前对所有参数进行求值。
class Complex{
public:
Complex(int flag){
this->flag = flag;
}
Complex& operator+=(Complex& complex){
this->flag = this->flag + complex.flag;
return *this;
}
bool operator&&(Complex& complex){
return this->flag && complex.flag;
}
public:
int flag;
};
int main(){
Complex complex1(0); //flag 0
Complex complex2(1); //flag 1
//原来情况,应该从左往右运算,左边为假,则退出运算,结果为假
//这边却是,先运算(complex1+complex2),导致,complex1的flag变为complex1+complex2的值, complex1.a = 1
// 1 && 1
//complex1.operator&&(complex1.operator+=(complex2))
if (complex1 && (complex1 += complex2)){
cout << "真!" << endl;
}
else{
cout << "假!" << endl;
}
return EXIT_SUCCESS;
}
根据内置&&的执行顺序,我们发现这个案例中执行顺序并不是从左向右,而是先右猴左,这就是不满足我们习惯的特性了。由于complex1 += complex2先执行,导致complex1 本身发生了变化,初始值是0,现在经过+=运算变成1,1 && 1输出了真。
符号重载总结
1.=, [], () 和 -> 操作符只能通过成员函数进行重载
2.<< 和 >>只能通过全局函数配合友元函数进行重载
3.不要重载 && 和 || 操作符,因为无法实现短路规则