运算符重载
重载运算符“+”
class Point {
int m_x, m_y;
public:
Point(int x, int y) : m_x(x), m_y(y) {}
void display() {
cout << "(x1=" << m_x << ", x2=" << m_y << ")" << endl;
}
// 第一个const表示返回值是个常量,用来防止 (p1+p2)=Point(10,20); 这种赋值
// 括号内第二个const表示传入的参数可以接收const也可以接收非const
// 第三个const表示常量可以调用该函数
// 具体见下面的分析。
const Point operator+(const Point &point) const {
return Point(m_x + point.m_x, m_y + point.m_y);
}
const Point operator+(const Point &point) const{
return Point(m_x + point.m_x, m_y + point.m_y);
}
这个函数定义有 3 个const,分别理解一下它们的意思。
第1个const
第一个const,表示返回值是常量,什么意思呢?我们来看一段C++代码。
int a = 1;
int b = 2;
(a + b) = 5; // 在C++中不允许如此做,会报错
上述这段代码在C++中是不允许的,a,b都是变量,但是无法对(a+b)进行赋值。
Point p1 = (10, 20);
Point p2 = (20, 30);
(p1 + p2) = Point(40, 50); // 如果不加第1个const,则这里不会报错
因此,对我们自定义的 Point 类来说,上面这段代码是应当是不允许的。解决方案是,我们将 (p1+p2) 的结果看作常数,常数不允许被赋值。因此,在重载操作数+的过程中,我们需要将返回值写成 const Point
。
第2个const
const Point operator+(const Point &point) const{
//code...
}
第2个 const ,即括号里的const Point &point
,这个 const 是什么意思呢?
这个 const 是为了扩大接受参数的范围。我们依旧来看一段C++代码。
int a = 1; // 变量
const int b = 2; // 常数
// b = 3; // 会报错,常数不可修改
int c = a + b ; // 常数不可修改,但是可以做运算
// 注意,这里a是变量,b是常数
上面这段代码表示,+ 号可以接收常数参数, 想想也很好理解,常数不可修改,但是可以参与运算。
Point p1 = (10, 20);
const Point p2 = (20, 30);
Point p3 = p1 + p2; // 不加第2个const,这里会报错,因为p2是常数
// 注意,这里p1是变量,p2是常量,由于重载了“+”
// 上一句代码等同于 p1.operator+(p2);
// 将p2视为了参数,即将常量视为参数。
对于我们自定义的类来说,上面这段代码应当是被允许的。我们需要在重载操作数+的过程中,给接收的参数加上 const,就可以同时接受 const参数 与 非const参数,扩大了接受参数的范围。
第3个const
const Point operator+(const Point &point) const{
//code...
}
第3个 const,即括号后面的 const,它的作用是 使得该函数可以被 const 对象所调用。直接看 Point 的操作。
const Point p1 = (10, 20);
Point p2 = (20, 30);
Point p3 = p1 + p2; // 不加第3个const,这里会报错,因为p1是常数
// 注意,这里p1是常量,p2是变量,由于重载了“+”
// 上一句代码等同于 p1.operator+(p2);
// 由p1调用的operator+函数,因此调用该函数的是个常数。
请仔细对比第2个const与第3个const的区别,仔细看注释!可能有点容易混淆!
p1对象是个常量,Point p3 = p1 + p2
; 实际上等价于 p1.operator+(p2)
;,实际上是拿常量p1调用了函数,因此这个函数必须可以被常量调用,这就是第3个const的作用。
优先队列
既然是队列那么先要包含头文件#include <queue>
,他和queue
不同的就在于我们可以自定义其中数据的优先级,让优先级高的排在队列前面,优先出队。
优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
和队列基本操作相同:
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- emplace 原地构造一个元素并插入队列
- pop 弹出队头元素
- swap 交换内容
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆
一般是:
//升序队列 小根堆
priority_queue <int,vector<int>,greater<int> > q;
//降序队列 大根堆
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
auto
auto temp = range[i];
总述:
auto的原理就是根据后面的值,来自己推测前面的类型是什么。
auto的作用就是为了简化变量初始化,如果这个变量有一个很长很长的初始化类型,就可以用auto代替。
注意点:
1.用auto声明的变量必须初始化(auto是根据后面的值来推测这个变量的类型,如果后面没有值,自然会报错)
2.函数和模板参数不能被声明为auto(原因同上)
3.因为auto是一个占位符,并不是一个他自己的类型,因此不能用于类型转换或其他一些操作,如sizeof
和typeid
4.定义在一个auto序列的变量必须始终推导成同一类型
auto x1 = 5, x2 = 5.0, x3='r'; // This is too much....we cannot combine like this
示例:
std::vector<std::string> ve;
std::vector<std::string>::iterator it = ve.begin();
我们可以用auto来代替那个初始化类型:
auto it = ve.begin();
此外,如果是可用迭代器的对象的话,还可以这样操作:
int main(){
vector<int>v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
for(auto i : v){
cout<<i<<" ";
}
cout<<endl;
return 0;
}
auto在这里就是简单的替换了int类型。
sort()
# include <algorithm>
using namespace std;
这个函数可以传两个参数或三个参数。第一个参数是要排序的区间首地址,第二个参数是区间尾地址的下一地址。也就是说,排序的区间是[a,b)。简单来说,有一个数组int a[100],要对从a[0]到a[99]的元素进行排序,只要写sort(a,a+100)就行了,默认的排序方式是升序。
如果是没有定义小于运算的数据类型,或者想改变排序的顺序,就要用到第三参数——比较函数。
比较函数是一个自己定义的函数,返回值是bool型,它规定了什么样的关系才是“小于”。想把刚才的整数数组按降序排列,可以先定义一个比较函数cmp
bool cmp(int a,int b)
{
return a>b;
}
C++pair类型
标准库类型–pair类型定义在utility头文件中定义
1、pair的创建和初始化
pair包含两个数值,与容器一样,pair也是一种模板类型。但是又与之前介绍的容器不同,在创建pair对象时,必须提供两个类型名,两个对应的类型名的类型不必相同
pair<string,string>anon;
pair<string,int>word_count;
pair<string, vector<int> >line;
当然也可以在定义时为每个成员提供初始化式:
pair<string,string>author("James","Joy");
pair类型的使用相当的繁琐,如果定义多个相同的pair类型对象,可以使用typedef简化声明:
typedef pair<string,string> Author;
Author proust("March","Proust");
Author Joy("James","Joy");
2、pair对象的操作
对于pair类,可以直接访问其数据成员:其成员都是公有的,分别命名为first和second,只需要使用普通的点操作符
string firstBook;
if(author.first=="James" && author.second=="Joy")
firstBook="Stephen Hero";
3、生成新的pair对象
除了构造函数,标准库还定义了一个make_pair函数,由传递给它的两个实参生成一个新的pair对象
pair<string, string> next_auth;
string first,last;
while(cin>>first>>last) {
next_auth=make_pair(first,last);
//...
}
还可以用下列等价的更复杂的操作:
next_auth=pair<string,string>(first,last);
由于pair的数据成员是公有的,因而可如下直接地读取输入:
pair<string, string> next_auth;
while(cin>>next_auth.first>>next_auth.last) {
//...
}
vector
//声明一个迭代器,来访问vector容器,作用:遍历或者指向vector容器的元素
vector<int>::iterator it;
for(it=obj.begin();it!=obj.end();it++)
{
cout<<*it<<" ";
}
CSDN上看到过[doge]
哈哈哈哈哈是的 觉得不错综合了一波