#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
//操作符重载除了左移和右移,其他最好定义在类里
class Complex
{
public:
friend Complex complexAdd1(Complex &c1, Complex &c2);
//friend Complex operator+(Complex &c1, Complex &c2);
//friend Complex operator-(Complex &c1, Complex &c2);
//friend Complex &operator+=(Complex &c1, Complex &c2);
//friend Complex &operator++(Complex &c);
//friend Complex operator++(Complex &c, int);
//friend Complex operator++(Complex &c, int);
friend ostream &operator<<(ostream &os, Complex &c);
friend istream &operator>>(istream &is, Complex &c);
Complex()
{
this->a = 0;
this->b = 0;
}
Complex(int a, int b)
{
this->a = a;
this->b = b;
}
//方法2:
Complex complexAdd2(Complex &another)//这只是一个方法,不是什么构造函数或者拷贝构造函数
{
Complex c(a + another.a, b + another.b);
return c;//也是返回匿名对象,并不是对象c,c会和此方法一起被回收
}
//方法4:
Complex operator+(Complex &another)
{
//Complex temp(a+another.a, b+another.b);
Complex temp;
temp.a = another.a;
temp.b = another.b;
return temp;
}
Complex &operator=(const Complex &another)
{
this->a = another.a;
this->b = another.b;
return *this;
}
Complex operator-(Complex &another)
{
//用类里对象的成员变量作最左边的值
Complex temp(a-another.a, b-another.b);
cout << "a=" << temp.a << endl;
cout << "b=" << temp.b << endl;
return temp;
}
Complex &operator+=(Complex &another)
{
a += another.a;
b += another.b;
return *this;//为了保持“+=”的性质,所以返回需要用引用返回
}
Complex &operator++()
{
this->a++;
this->b++;
return *this;
}
Complex operator++(int)
{
Complex temp(a, b);
a++;
b++;
return temp;
}
void *operator new(size_t size)
{
cout << "complex size=" << size << endl;
return malloc(size);
}
int operator()(int value)
{
cout << "complex ()" << endl;
return value * value;
}
/*
//这个中形式是错误的,下面一段代码的本质意义为c1.operator<<(cout),即为c1 << cout;
//因此“<<”操作符重载不能写在类的内部,否则调用的顺序会变反,c1 << cout;
void operator<<(ostream &os)
{
os << "a=" << a << endl;
os << "b=" << b << endl;
return;
}
*/
private:
int a;
int b;
};
//方法1:
Complex complexAdd1(Complex &c1, Complex &c2)
{
Complex temp(c1.a + c2.a, c1.b + c2.b);//这是构建一个新的对象
return temp;//并不是返回temp,而是调用拷贝构造函数,构建了一个匿名对象,然后把temp释放,返回的是匿名对象
}
//方法3:操作符重载写在全局
//操作符重载写在全局时,此操作符不仅包含重载的意义,也包含之前的意义,编译器会自己去比较操作符两边,判断操作符该不该重载
//操作符只能重载一次,不论是在类里还是在全局,都算上也只能重载一次
/*
Complex operator+(Complex &c1, Complex &c2)//operator为操作符重载关键字,后面跟什么符号就重载什么符号,这里后面跟的是+好,所以重载的是+号操作符
{
Complex temp(c1.a + c2.a, c1.b + c2.b);//友元完全独立,任何都不传递,因此需要重新声明这个函数为类的友元函数
return temp;
}
*/
/*
Complex operator-(Complex &c1, Complex &c2)
{
Complex temp(c2.a-c1.a, c2.b-c1.b);
return temp;
}
*/
//"+="运算符重载
//全局时
/*
Complex &operator+=(Complex c1, Complex c2)//要区分清什么时候可以返回引用,什么时候不可以,局部变量的时候不可以返回引用,函数结束,变量不被回收的对象可以返回引用
{
c1.a += c2.a;
c1.b += c2.b;
return c1;//需要理解"+="的实际意义,需要返回本身
}
*/
//"++"单目运算符的重载
//全局时
/*
//正常些的时候,等价于++c,前++
Complex &operator++(Complex c)//全局时,单目即就其本身一个参数,双目就两个参数
{
++(c.a);
++(c.b);
return c;//因为其也是可以连加加的,所以返回其本身的引用
}
*/
//后++运算符重载的时候,需要占位符,且后++运算符不能连加加,但前加加运算符可以连加加
/*
Complex operator++(Complex &c, int)
{
Complex temp(c.a, c.b);//后加加的时候,需要保存加加前的值,然后值再加加,因此需要重新构造一个对象,代替c加加前的值,然后返回这个对象,但不能返回引用
c.a++;
c.b++;
return temp;
}
*/
//左移操作符重载只能写在全局中,不可以写在类的内部
ostream &operator<<(ostream &os, Complex &c)
{
//os在这里即为cout,这里的“<<”操作符在这里的意义仍为系统定义的意义,自己定义的意义只能有一种,编译器会根据操作符两边的数据类型自动匹配是运用重载的还是系统定义好的操作符
os << "c.a=" << c.a << endl;
os << "c.b=" << c.b << endl;
//因为左移操作符可以连续左移,所以需要返回引用本身
return os;//返回的变量为引用时,只能返回它的引用,不可以返回变量本身,因为类型要一致,且变量名为引用,返回时为变量没有任何意义
}
//右移操作符同样只能写在全局中,不能写在类的内部
istream &operator>>(istream &is, Complex &c)
{
cout << "a=";
is >> c.a;
cout << "b=";
is >> c.b;
return is;
}
class Student
{
public:
friend ostream & operator<<(ostream &out, Student &s);
Student()
{
this->id = 0;
this->name = NULL;
}
Student(int id, const char *name)
{
this->id = id;
int len = strlen(name);//这儿如果用sizeof就为4了,不能用sizeof,因为这为指针,不是数组
this->name = new char[len + 1];
strcpy(this->name, name);
}
Student(int id, int age)
{
this->id = id;
this->age = age;
this->name = NULL;
}
Student(const Student &another)
{
this->id = another.id;
this->age = another.age;
/*
//下面两段代码的写法是完全错误的,指针point指向NULL,因此不可用strlen()函数计算指向空间的长度
char *point = NULL;
strlen(point);
*/
if (another.name != NULL)//这里的this是匿名对象,another是对象temp,虽然temp.name=NULL,但匿名对象的name指针你没有给它指向,它是随机指向别的空间的,但一定不是指向NULL,因此在这儿也要把匿名对象的name指针指向NULL
{
int len = strlen(another.name);
this->name = new char[len + 1];//加1是为了在最后一位补/0
strcpy(this->name, another.name);
}
else
{
this->name = NULL;
}
}
Student operator+(const Student &another)
{
Student temp(this->id + another.id, this->age + another.age);
//temp.id = this->id + another.id;
//temp.age = this->age + another.age;
return temp;//返回匿名变量时,会调用拷贝构造函数构建匿名对象,一定要注意,匿名对象的操作和拷贝构造函数紧密相关
}
Student &operator=(const Student &another)
{
//1.防止自身给自身赋值,等号两边为同一对象,因此不存在释放两次
if (this == &another)//比较两对象地址是否相同,同一块地址肯定指的是同一个对象,判断是否是同意对象,不能通过值来判断,只能通过地址来判断
{
return *this;
}
//2.如果自身开辟了空间,先把开辟的空间释放
if (this->name != NULL)
{
delete[] this->name;
}
//3.剩下执行深拷贝动作就可以了
this->id = another.id;
this->age = another.age;
if (another.name != NULL)//这儿的another对象为拷贝temp的匿名对象
{
int len = strlen(another.name);
this->name = new char[len + 1];
strcpy(this->name, another.name);
}
return *this;
}
//操作符重载也可以重载,因为c++编译函数时,会编译函数名,函数参数类型和个数
int operator()(int value)
{
cout << "student ()" << endl;
return value * value;
}
int operator()(int value1, int value2)
{
return value1 * value2;
}
//new和delete操作符也可以重载,但很少重载
//void *和void没有任何关系,void *表示万能指针,可以指向任何类型,viod表示没有类型
void *operator new(size_t size)//typedef unsigned int size_t;size指开辟的空间,这儿的size只是类型大小,由编译器自动帮我们从类型换为所占空间大小
{
cout << "operator new" << endl;
cout << "student size=" << size << endl;
//new操作符重载虽然是用malloc函数开辟空间,但它仍调用构造函数
return malloc(size);
}
void operator delete(void *p)//这是的函数参数为万能指针,即类型为void *
{
//delete是释放类里指针指向的空间,与析构函数完全不同
if (p != NULL)
{
//delete操作符重载虽然用free函数实现,但仍会调用析构函数
free(p);//delete[] p;使用delete释放指针指向的空间时不用再使p=NULL,但使用free(p);释放指针指向的空间时,需要使指针指向空
p = NULL;
}
}
~Student()
{
if (this->name != NULL)
{
delete[] this->name;
this->name = NULL;//我认为这一段代码是没有必要的,因为析构函数是最后对象被回收时触发的,此时这个对象都被回收了,那么显然对象里的name指针也被回收了,对一个即将不存在的变量赋值显然是没有任何意义的
}
}
private:
int id;
int age;
char *name;
};
ostream &operator<<(ostream &out, Student &s)
{
out << "s.id=" << s.id << endl;
out << "s.age=" << s.age << endl;
out << "s.name=" << s.name << endl;
return out;
}
class Test
{
public:
Test()
{
this->a = 0;
this->b = 0;
}
Test(int a, int b)
{
this->a = a;
this->b = b;
}
Test(const Test &another)
{
cout << "copy function" << endl;
this->a = another.a;
this->b = another.b;
}
Test &operator=(const Test &another)
{
this->a = another.a;
this->b = another.b;
cout << "test operator=()" << endl;
return *this;
}
Test operator+(const Test &another)
{
Test temp;
temp.a = this->a + another.a;
temp.b = this->b + another.b;
cout << "test operator+()" << endl;
return temp;
}
private:
int a;
int b;
};
class Box
{
public:
Box()
{
this->volume = 0;
}
Box(int volume)
{
this->volume = volume;
}
bool operator&&(const Box &another)
{
if ((this->volume != 0) && (another.volume != 0))
{
return true;
}
else
{
return false;
}
}
bool operator||(const Box &another)
{
if ((this->volume !=0) || (another.volume != 0))
{
return true;
}
else
{
return false;
}
}
private:
int volume;
};
int main()
{
Complex c1(1, 2);//要区分清是建立对象还是调用函数,构建对象与调用函数其实是完全不同的,构建对象有类名,会有空格,但调用函数就直接函数名加参数了
Complex c2(2, 3);//构建对象:Complex c1(1, 2);调用函数:c1(1, 2);
//方法1:
Complex c3 = complexAdd1(c1, c2);//此时d等价于c3 = c1 + c2;
//方法2:
Complex c4 = c1.complexAdd2(c2);//这儿便不会再调用拷贝构造函数了,当有对象来接收匿名对象的时候,直接把匿名对象命名为这个对象名称
//方法3:
Complex c5 = c1 + c2;
//当调用方法3进行操作符重载时:c1+c2等价于operator+(c1, c2);其实就相当于调用了上面的操作符重载函数
//当调用方法4进行操作符重载时:c1+c2等价于c1.operator+(c2);
Complex c6 = c2 - c1;//c2-c1等价于operator-(c2-c1),等价于c2.opreator-(c1);
//操作符重载可以连续使用,但返回时不能是引用,应是匿名变量
//即:c = c1-c2-c3;
//c++中只有4个操作符不能重载“.”“::”“:?”(条件运算符)“.*”
//重载不能改变运算符之前的性质,即左右参数个数,优先级等
//操作符重载时,最少要有一个自定义类型,常规类型的操作符修改没有任何意义,因此操作符重载与参数类型有直接关系
//“=”和“&”一般不进行操作符重载,“=”除了在深拷贝的时候
(c1 += c2)+=c2;//要保持+=原本的性质,可以连加等
++c1;//可以连加加,即:++++c1;
c1++;//不可以连加加,即:c1++++;是错误的
cout << c1;//等价于operator<<(cout, c1);这里cout也为一个对象,为ostream类型,因此左移操作符为双目运算符
//cin >> c1;
Student s1(10, "111");
Student s2 = s1;//等价于Student s2(s1);调用的是拷贝构造函数
Student s3;
//当类有存在指针成员变量时,需要重写“=”等号操作符
s3 = s2 = s2;//调用的是赋值操作符,是把s1里的私有成员变量的值一一赋给s3的私有成员变量,并不是调用拷贝构造函数,等号是可以连等的
Student s4(11, "hello");
int value = s4(10);//这是重写了操作符(),并不是调用构造函数,因为形式和普通函数形式一样,因此称为仿函数,伪函数,其本质是操作符“()”的重载,在STL里有大量运用
value = s4(1, 2);
value = c1(1);
cout << "sizeof(Student)=" << sizeof(Student) << endl;
Student *p = new Student;//这是调用new操作符重载进行开辟空间,编译器先通过new右边的类型去这个类里匹配new操作符有没有重载,如果有就重载new
Complex *ppp = new Complex;//Student和Complex类里的new操作符重载是不冲突,不发生歧义的
int *pp = new int;//这是调用没有重载过的new进行空间开辟,编译器会根据类型自动帮我们匹配是否进行操作符重载
cout << s4;
Student s5 = s1 + s2;
s5 = s1 + s2;
Test t1(1, 2);
Test t2(2, 3);
Test t3;
t3 = t1 + t2;//这是赋值,会调用等号操作符重载
Test t4 = t1 + t2;//这是对象初始化,会调用拷贝构造函数,不会调用等号操作符重载,调用拷贝构造函数是为了构建匿名对象
Complex c10;
Complex c11(1, 2);
Complex c12;
c12 = c10 + c11;
Box b1;
Box b2(2);
//if只能识别bool类型,所以操作符重载返回类型为bool类型
if (b1 && b2)//但不能发生短路,即当b1不成立时,b2就不会执行,但当b2=b3+b4时,后面之和一定会执行,并不会发生短路,且避免不了,因此不建议重载||和&&操作符
{
cout << "t1 && t2" << endl;
}
if (b1 || b2)
{
cout << "t1 || t2" << endl;
}
return 0;
}
//操作符重载总结:
//1.左移和右移操作符重载只能写在类的外面,定义成全局函数,其他的类里和类外都可以
//2.有4个操作符不能重载“.”“::”“:?”(条件运算符)“.*”
//3.一个程序里一种操作符可以重载多次,在不同类里的相同操作符重载并不影响,因为类内部操作符重载的作用域只在类内部,肯定不互相影响,编译器会先从类里帮我们匹配,再从全局帮我们匹配
// 在类外部时,相同操作符的重载只要操作符两边对象的类型不同时,也不会发生歧义,因为编译器会自动帮我们匹配操作符两边参数类型,进而帮我们唯一匹配到重载的操作符
//4.=号操作符重载,其函数参数一定要用const修饰
//5.对于类内部含有指针的对象进行=号操作符重载,需要先判断是不是给自己赋值,再判断本对象的指针指向是否为空,为空则需要先释放指向的空间,再判断赋值的对象的指针是否为空,为空就不需要new开辟空间cpy赋值了
//6.new和delete操作符也可以重载,new操作符重载函数里的形参为无符号整形,形参数值为我们传入类型的大小,编译器会自动帮我们计算出类型大小并赋值给size,new和delete即使重载了,也会触发构造和析构函数
//7.不建议重载操作符&&和||,因为重载过的&&和||操作符不能发生短路现象,且没法解决,因此最好不重写这两个操作符