文章目录
- 一、运算符重载
-
- 二、加号运算符重载
- 1.code格式
- (1)通过成员函数实现加号运算符重载
- (2)通过全局函数实现加号运算符重载
- 2.作用
- 3.拓展
-
- 三、左移运算符重载
-
- 四、递增运算符重载
-
- 五、赋值运算符重载
- (1)浅拷贝&深拷贝
- (2)自定义拷贝构造函数解决浅拷贝问题
- (3)赋值运算符重载解决浅拷贝问题
- 六、关系运算符重载
-
- 七、函数调用运算符重载(重点)
-
- 八、全文总结
Note:
i.视频为
黑马程序员C++视频(121-126),系列文章为视频听课笔记;
ii.难度指数:++
iii.不论变量、函数名、标识符形式怎样复杂,只要我们考虑编程的本质是对内存的操作,对内存进行分析,一切逻辑都会变得清晰。
一、运算符重载
1.定义
对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
二、加号运算符重载
1.code格式
(1)通过成员函数实现加号运算符重载
通过成员函数重载加号,实现两个示例对象属性相加并返回拥有相加属性的新的对象。
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int m_A;
int m_B;
person operator +(person &p)
{
person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
};
void test01()
{
person p1;
p1.m_A = 10;
p1.m_B = 20;
person p2;
p2.m_A = 10;
p2.m_B = 20;
person p3 = p1 + p2;
cout << p3.m_A << endl;
cout << p3.m_B << endl;
}
int main()
{
test01();
}
(2)通过全局函数实现加号运算符重载
通过全局函数重载加号,实现两个示例对象属性相加并返回拥有相加属性的新的对象。
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int m_A;
int m_B;
};
person operator + (person &p1, person &p2)
{
person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
void test01()
{
person p1;
p1.m_A = 10;
p1.m_B = 20;
person p2;
p2.m_A = 10;
p2.m_B = 20;
person p3 = p1 + p2;
cout << p3.m_A << endl;
cout << p3.m_B << endl;
}
int main()
{
test01();
}
2.作用
实现两个自定义数据类型(非编译器内置数据类型)的相加。
3.拓展
(1)运算符重载与函数重载可以同时发生
上述加号运算符重载涉及到operator +
函数,也能实现函数重载。
函数重载:函数重载是函数名相同,但传入参数类型等不同的情况下能够实现对不同函数的调用。
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int m_A;
int m_B;
};
person operator + (person &p1, person &p2)
{
person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
person operator + (person &p1, int num)
{
person temp;
temp.m_A = p1.m_A + num;
temp.m_B = p1.m_B + num;
return temp;
}
void test01()
{
person p1;
p1.m_A = 10;
p1.m_B = 20;
person p2;
p2.m_A = 10;
p2.m_B = 20;
person p3 = p1 + p2;
cout << "p3.m_A=" << p3.m_A << endl;
cout << "p3.m_B=" << p3.m_B << endl;
person p4 = p1 + 5;
cout << "p4.m_A="<< p4.m_A << endl;
cout << "p4.m_B="<< p4.m_B << endl;
}
int main()
{
test01();
}
该图为上述代码的输出,由输出可见,由于加号运算符前后数据类型不同,在进行加法运算时,加号运算符重载时调用的是两个不同的全局函数。
【一点思考】:
1)重载类似于利用同一个标志符,但使其在不同情况下发挥的作用不同。例如运算符重载是指运算符相同,但数据类型不同时能够实现自定义数据类型的运算;函数重载是函数名相同,但是参数类型等不同的情况下能够实现对不同函数的调用。
2)加号运算符的重载通过定义一个名为operator +
的成员函数或者全局函数来实现,当需要定义多个operator +
函数实现自定义类型数据的相加运算时,也会发生函数重载的现象。
三、左移运算符重载
左移运算符即<<
。
1.code格式
(1)通过全局函数实现左移运算符重载
通过全局函数重载左移运算符,实现自定义数据类型——类的输出。
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int m_A;
int m_B;
};
void operator << (ostream &out,person &p)
{
out << p.m_A << endl;
out << p.m_B << endl;
}
void test01()
{
person p;
p.m_A = 10;
p.m_B = 20;
cout << p;
cout << p << "hello world" << endl;
}
int main()
{
test01();
return 0;
}
代码示例一运行结果:
代码示例一能够实现<<
重载,但不能实现不断重载,即cout << p <<...
会报错,这是因为<<
重载函数返回类型是void
,void
不能继续对重载函数进行调用。要实现对重载函数的不断调用,只要实现重载函数返回值为cout即可,具体代码见示例二。
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int m_A;
int m_B;
};
ostream & operator << (ostream &out,person &p)
{
out << p.m_A << endl;
out << p.m_B << endl;
return out;
}
void test01()
{
person p;
p.m_A = 10;
p.m_B = 20;
cout << p << "hello";
}
int main()
{
test01();
return 0;
}
代码示例二运行结果:
2.作用
可以输出自定义数据类型。
【总结】
1)左移运算符的重载通过定义一个名为operator <<
的全局函数来实现;
2)这里出现了新的数据类型,即cout
的数据类型:ostream
;
cout
能作全局函数的参数也是我没想到的hiahiahia
3.拓展
思考:当上述代码示例中类的属性为私有属性时,怎么输出?
答:利用全局函数作友元,在类中加上friend
+全局函数声明即可。代码见示例三:
#include<iostream>
#include<string>
using namespace std;
class person
{
friend ostream & operator << (ostream &out, person &p);
friend void test01();
private:
int m_A;
int m_B;
};
ostream & operator << (ostream &out,person &p)
{
out << p.m_A << endl;
out << p.m_B << endl;
return out;
}
void test01()
{
person p;
p.m_A = 10;
p.m_B = 20;
cout << p << "hello";
}
int main()
{
test01();
return 0;
}
四、递增运算符重载
1.code格式
(1)前置递增运算符重载
前置递增运算符的作用是:先递增,后执行表达式,具体示例见代码中main
函数的前三行。
代码如下所示:
#include<iostream>
#include<string>
using namespace std;
class MyInteger
{
friend ostream & operator << (ostream &out, MyInteger MyInt);
public:
MyInteger()
{
m_num = 0;
}
MyInteger& operator ++()
{
this->m_num++;
return *this;
}
private:
int m_num;
};
ostream & operator << (ostream &out, MyInteger MyInt)
{
cout << MyInt.m_num<< endl;
return out;
}
void test01()
{
MyInteger myint;
cout << ++(++myint) << endl;
cout << myint << endl;
cout << ++(++myint) << endl;
cout << myint << endl;
}
int main()
{
int a = 0;
cout << ++a << endl;
cout << a << endl;
test01();
return 0;
}
【关于前置递增运算符重载函数返回必须为引用的原因】: 如果重载函数返回值非引用,那么每次++myint
后都会创建一个新的MyInteger
对象,再进行递增就不是基于实例对象myint
了,而是在每次++
后创建的新的MyInteger
实例对象上。
(2)后置递增运算符重载
后置递增运算符的作用是:先执行表达式,后递增。具体示例见代码中main
函数前三行。
代码如下所示:
#include<iostream>
#include<string>
using namespace std;
class MyInteger
{
friend ostream & operator << (ostream &out, MyInteger myint);
public:
MyInteger()
{
m_num = 0;
}
MyInteger operator ++(int)
{
MyInteger temp = *this;
this->m_num++;
return temp;
}
private:
int m_num;
};
ostream & operator << (ostream &out, MyInteger myint)
{
cout << myint.m_num;
return out;
}
void test01()
{
MyInteger myint;
cout << myint++ << endl;
cout << myint << endl;
}
int main()
{
int a = 0;
cout << a++ << endl;
cout << a << endl;
test01();
return 0;
}
【关于重载函数为什么不能返回引用】:因为temp
为局部变量,函数执行结束后即释放。在引用中我们提到,引用作为函数返回值时,不能返回局部变量的引用。
2.作用
通过递增运算符重载,实现自定义的整型数据递增,例如类中某整型成员属性的的递增。同理,上述代码也可进行递减运算符的重载,进而实现自定义整型数据的递减。
五、赋值运算符重载
(1)浅拷贝&深拷贝
【深浅拷贝的起源&图解&解决方法】:
【起源】:前面学到,c++在创建类时会默认分配三个函数
1)默认构造函数(无参,函数体为空)
2)默认析构函数(无参,函数体为空)
3)默认拷贝构造函数(实现对属性的值拷贝)
然而,当创建的类中包含堆区指针数据的成员属性时,若调用默认拷贝构造函数(即浅拷贝)会导致堆区指针内存的重复释放(释放由析构函数实现),进而导致程序无法运行。
实际上,c++在创建类时还会默认分配赋值运算符重载函数,即operator =
函数。
【动态图解】:
【解决方法】:
1)自定义拷贝构造函数,实现深拷贝
2)赋值运算符重载
下面我们将从解决浅拷贝方法的角度来介绍赋值运算符重载。
(2)自定义拷贝构造函数解决浅拷贝问题
自定义拷贝构造函数见代码中所示,其开辟了新的堆区内存储存实例对象p1
中的成员属性,实现了将实例对象p
中指针数据m_age
指向的值赋值给p1
,而不是单纯的将m_age
赋值。
#include<string>
#include<iostream>
using namespace std;
class person
{
public:
person(int age)
{
cout << "构造函数调用" << endl;
m_age = new int(age);
}
person(const person &p)
{
cout << "自定义拷贝函数调用" << endl;
m_age = new int(*(p.m_age));
}
~person()
{
cout << "析构函数调用" << endl;
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
int * m_age;
};
void test01()
{
person p(18);
person p1(p);
cout << "p的年龄为:" << *p.m_age << endl;
cout << "p1的年龄为:" << *p1.m_age << endl;
}
int main()
{
test01();
return 0;
}
上述代码执行的输出结果如下所示:
(3)赋值运算符重载解决浅拷贝问题
赋值运算符重载函数见代码中person & operator = (person &p)
所示:
【思考】:
要学会分析定义重载函数返回值为不同类型的逻辑,如下所示代码中,若定义赋值运算符重载函数的返回值类型为person
类,则执行一次重载函数,都会创建一个新的person
实例对象。
#include<string>
#include<iostream>
using namespace std;
class person
{
public:
person(int age)
{
cout << "有参构造函数调用" << endl;
m_age = new int(age);
}
~person()
{
cout << "析构函数调用" << endl;
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
person & operator = (person &p)
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
m_age = new int(*p.m_age);
return *this;
}
int * m_age;
};
void test01()
{
person p(18);
person p1(20);
p1 = p;
cout << "p的年龄为:"<<*p.m_age<<endl;
cout << "p1的年龄为:" << *p1.m_age << endl;
}
int main()
{
int a = 10;
int b = 20;
int c = 30;
c = b = a;
cout << "a的值为" << a << endl;
cout << "b的值为" << b << endl;
cout << "c的值为" << c << endl;
person p1(10);
person p2(20);
person p3(30);
p3 = p2 = p1;
cout << "p1的值为" << *p1.m_age << endl;
cout << "p2的值为" << *p2.m_age << endl;
cout << "p3的值为" << *p3.m_age << endl;
return 0;
}
六、关系运算符重载
(1)code格式
以关系运算符“==”和“!=”为例,核心函数为bool operator ==(person &p)
和bool operator !=(person &p)
。
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
person(int age, string name)
{
m_age = age;
m_name = name;
}
bool operator ==(person &p)
{
if (this->m_age == p.m_age && this->m_name == p.m_name)
{
return true;
}
else
return false;
}
bool operator !=(person &p)
{
if (this->m_age != p.m_age || this->m_name != p.m_name)
{
return true;
}
else
return false;
}
int m_age;
string m_name;
};
void test01()
{
person p1(18, "TOM");
person p2(25, "吴晗");
person p3(18, "TOM");
if (p1 == p3)
{
cout<<"p1和p3是相等的"<<endl;
}
if (p1 != p2)
{
cout << "p1和p2是不相等的" << endl;
}
}
int main()
{
test01();
return 0;
}
(2)作用
重载任意关系运算符,实现自定义数据类型的对比。
七、函数调用运算符重载(重点)
(1)code格式
函数调用运算符即()
,该部分涉及()的重载。
#include<iostream>
#include<string>
using namespace std;
class Myprint
{
public:
void operator ()(string zfc)
{
cout << zfc << endl;
}
};
class Myadd
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
};
void test01()
{
Myprint p1;
p1("hhhhhhhhhhhh");
Myprint()("吴晗");
}
void test02()
{
Myadd shs;
int sum1 = shs(12, 13);
cout << "两数之和=" << sum1 << endl;
int sum2 = Myadd()(19, 81);
cout << "两数之和=" << sum2 << endl;
}
int main()
{
test01();
test02();
return 0;
}
(2)作用
知道运算符()也可以重载,了解仿函数以及匿名函数对象,后面学习STL会经常用到。
【总结】:
1)仿函数:由于运算符()重载函数的使用与函数调用类似,因此其在使用时被称为仿函数。
2)匿名函数对象:类名+()即为一个匿名函数对象,执行完后立即释放。
八、全文总结
C++【类与对象】——运算符重载串讲,涉及:
1)加号运算符,+:实现自定义数据类型相加
2)左移运算符,<<:实现自定义数据类型输出
3)递增运算符,前置++、后置++:实现自定义数据类型递增
4)关系运算符,==、!=:实现自定义数据类型比较操作
5)函数调用运算符,():定义()实现自定义功能
核心函数为operator加上述运算符,难点为重载运算符的连续调用。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)