C++初步之核心编程篇二:重载
一、函数重载
对于函数来说,重载定义如下:
函数重载是一种特殊情况,C++允许在同一作用域中声明几个类似的同名函数,这些同名函数的形参列表(参数个数,类型,顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
函数重载的满足条件:
- 同一个作用域下
- 函数名相同
- 参数类型或个数不同
注意:函数的返回值不可作为函数重载的条件!
引用可以作为重载的条件
函数重载遇到默认参数,有时会出现二义性(编译通过,调用出错)
如:
int func(int a) {cout << a << endl;}
int func(int a, int b = 0) {cout << a << " " << b << endl; }
func(3);//报错,因为编译器不知道进入哪个
-
普通函数
int func(int a) { cout << a << endl; } int func(int a, int b) {cout << a << ' ' << b << endl; }
-
模板函数
template <class T> void func(T a); template <class T1, class T2> void func(T1 a, T2 b);
二、运算符重载
重载的两种方法:
- 通过成员函数重载
- 通过全局函数重载
以下均以person类为例
/*
person.hpp
*/
class Person
{
public:
Person(string name, int age);
string getName();
int getAge();
~Person();
private:
string m_name;
int age;
}
Person::Person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
Person::string getName()
{
return this->m_name;
}
Person::int getAge()
{
return this->age;
}
Person::~Person()
{
}
1、加、减号
对于加减号的重载,以上两种重载方法均适用
person p1, p2;
p1 + p2 ?
-
通过成员函数进行重载
Person operator+ (Person &p) { Person temp; temp.m_age = this->m_age + p.m_age; temp.m_name = this->m_name + p.m_name;//新疆地区取名习俗doge return temp; }
-
通过全局函数进行重载
Person operator+(Person &p1, Person &p2) { Person temp; temp.m_age = p1.m_age + p2.m_age; temp.m_name = p1.m_name + p2.m_name; return temp; }
-
测试:
Person p1("张三", 22), p2("李四", 23); Person p3 = p1.operator+(p2);//简写成:Person p3 = p1 + p2;
减号同理
2、左、右移运算符
只能用全局函数重载左运算符
void operator <<(ostream &cout, Person &p)
{
cout << "m_age = " << p.m_age << endl << "m_name = " << p.m_name << endl;
}
如果按照上面的重载方式,就变成了:
Person p("张三", 24);
cout << p;//可以运行
//但是,就不能连续地输出了,如:
cout << p << 10; //这是错的
改进版本:
ostream &operator <<(ostream &out, Person &p)
{
cout << "m_age = " << p.m_age << endl << "m_name = " << p.m_name << endl;
return out;
}
这样,完成一次输出后,因为又返回来输出本身的引用,故可以连续输出。
右移运算符类似
3、 前、后自增运算符
-
前自增:
Person & operator ++() { m_age ++;//先进行++运算 return *this;//再返回自身 }
-
后自增:
Person & operator ++(int) { Person temp = *this;//先记录当时结果 m_age ++;//后递增 return temp;//最后将记录的结果返回 }
自减运算类似
4、赋值运算符
Person & operator =(Person &p)
{
this->m_age = p.m_age;
this->m_name = p.m_name;
}
上述赋值运算仅涉及浅拷贝,对于在堆区开辟内存的变量,需要用到深拷贝,
例如:
如果m_age, m_name是这样的,变量开辟在堆区
int *m_age, string *m_name Person(string name, int age) { m_age = new int(age); m_name = mew string(name); return *this; }
则,赋值操作应该改为深拷贝:
Person & operator =(Person &p) { if (m_age != NULL) { delete m_age; m_age = NULL; } if (m_name != NULL) { delete m_name; m_name = NULL; } m_age = new int(*p.m_age); m_name = new string(*p.m_name); return *this; }
5、关系判断(小于号)
-
使用背景:常用于配合STL使用,例如快排、优先队列等,快排默认从小到大排序,优先队列默认大根堆
例如:arr[6] = {3, 1, 2, 6, 5 , 4};
sort(a, a + 6);
==> 1 2 3 4 5 6;
priority_queue < int> heap;//arr[]数组全部放进heap中,然后一个一个出队得到顺序:
==> 6 5 4 3 2 1
当然,这里只要说明一下比较方式,就能按照大/小进行排序,如:
sort(a, a + 6, greater< int>()) ;//按照从大到小排序
==> 6 5 4 3 2 1
sort(a, a + 6, less < int> ()); //按照从小到大排序
==> 1 2 3 4 5 6
priority_queue<int, vector< int>, greater< int> > heap;
//arr[]数组全部放进heap中,然后一个一个出队得到顺序:
==> 1 2 3 4 5 6
但是,以上都是已知的数据类型,对于自定义数据类型,编译器不知道如何比较这两个值,如:
class person { public: int m_a, m_b; string m_str; }; person p1(1,2,"123"), p2(1, 3, "456");
但是p1 ?p2的关系就不知道了,是按照m_a属性比较还是m_b还是m_string?
这时候的比较关系,需要添加,告诉编译器怎么比,重载小于号就是在原有关系比较上增加一种方法,使得编译器即能保持对原来数据关系的比较,也能实现对新的数据类型大小关系的判断。
-
特别说明:对于关系比较,重载小于号即可,重载大于号可能会出错,(有的说重载大于号会编译出错,因为标准库默认使用元素类型的小于号操作符来确定它们之间的优先级关系,但我确实见过优先队列重载大于号成功的…)。
-
方式选择:
-
类/结构体内重载
struct Node { int a, b; bool operator< (const Rec &t)const { return a < t.a } };
-
cmp自定义比较函数
sort函数、优先队列中cmp函数不同,优先队列重载比较关系更为复杂
优先队列重载相关:https://www.acwing.com/file_system/file/content/whole/index/content/1638282/
-
THE END…