C++的输入输出特点、运算符重载及标准模板库STL

上年跟学长学STL时写的博客,由于刚学很多不懂,很多地方解释不详细,现在来填一下坑,力求准确详细吧!——2020年 6 月1 日

1.C++输入与输出

1.1头文件:

#include <iostream>

1.2头文件与主函数之间:

using namespace std;

使用(using)名空间(namespace)std,std是名空间的名字,这是C++为了解决不同工程的变量,函数,类等命名冲突的问题,引入的名空间(namespace)的概念,相当于文件夹的目录和子文件的关系——不同的目录(namespce)下即使有相同子文件名/文件夹名(变量,函数,类)也不会产生冲突(记住别忘加就行!)

1.3输入cin:

1.3.1 cin输入的原理

程序的输入都建有一个缓冲区,即输入缓冲区。一次输入过程是这样的,当一次键盘输入结束时会将输入的数据存入输入缓冲区,而cin函数直接从输入缓冲区中取数据。正因为cin函数是直接从缓冲区取数据的,所以有时候当缓冲区中有残留数据时,cin函数会直接取得这些残留数据而不会请求键盘输入。
注意:cin>>和cin.get()都残留数据不会出错,但是cin.getline会报错,下面的示例中都有体现。

1.3.2 cin >>

存储变量类型:char,int,string都可以;
输入结束条件:遇到Enter、Space和Tab键。(如含有空格的字符串无法完全读取)
对结束符处理:丢弃缓冲区中使得输入结束的结束符(Enter、Space和Tab),但最后输入结束时不会丢弃结束字符(具体见代码)
实例:

#include<iostream>
using namespace std;
int main()
{
    char c1,c2,c3;
    cin >> c1;
    cin >> c3;
    cin.get(c2);
    cout << c1 << endl;
    cout << c3 << endl;
    cout << c2 << 44 << endl;
    return 0;
}

在这里插入图片描述

解释:输入为1[Space]2[Enter],cin >>先读取1存入c1 由于缓冲区还有数据,cin >> 先清除[Space] 然后读取2存入c3,后面没有数据了,结束读取并保留缓存区里剩下的数据(即换行符[Enter]),用cin.get()读取存入c2,依次输出可得图示结果。

1.3.3 cin.get()

存储变量类型:char。
输入结束条件:Enter键(因此可接受空格和Tab键)。
对结束符处理:不丢弃缓冲区中的Enter。
使用方法:

  • ch=cin.get() 或 cin.get(ch)
  • cin.get(数组名,长度,结束符):
    结束符为可选参数,读入的字符个数最多为长度-1个,结束符规定结束字符串读取的字符,默认为enter。
    输入结束条件:结束符或者输入大于指定长度-1时;
    读取时对字符的处理:不跳过空格,tab,enter,读取长度小于指定长度-1的字符,直至遇到enter结束;
    结束时不丢弃任何字符故可用于带空格的字符串的输入
1.3.4 cin.getline()

cin.getline(数组名,长度,结束符) 大体与 cin.get(数组名,长度,结束符)类似。
区别在于:
cin.get()当输入的字符串超长时,不会引起cin函数的错误,后面的cin操作会继续执行,只是直接从缓冲区中取数据。但是cin.getline()当输入超长时,会引起cin函数的错误,后面的cin操作将不再执行。 cin.getline()是以enter为结束标志的,同时丢弃了enter

1.4输出:cout

cout << a << b << endl; //endl为换行符
推荐此博客,比较详细,特殊的输出个人觉得用printf比较方便(如保留小数输出)

2.C++的重载

2.1重载函数

C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,即函数的参数列表不同,也就是说用同一个运算符完成不同的运算功能。这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。当函数的编写者充分考虑了不同情况下应该运行稍有不同的函数,函数的使用者就不必为这些小细节而烦恼了。

2.1.1例:

void print(int a)
void print(string a) //参数类型不一样
void print(int a ,string b); //参数个数不同
void print(string a ,int b);//参数顺序不同

2.2重载运算符

您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。重载的运算符是带有特殊名称的函数,函数名是由关键字operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。

2.2.1实现一个操作符重载的方式通常分为两种情况:

  • 将操作符重载实现为类的成员函数;
  • 操作符重载实现为非类的成员函数(即全局函数)。
2.2.1.1将操作符重载实现为类的成员函数

在类体中声明(定义)需要重载的操作符,声明方式跟普通的成员函数一样,只不过操作符重载函数的名字是“关键字 operator +以及紧跟其后的一个C++预定义的操作符”,代码如下:

struct node 
{
    int x,y;
    bool operator < (node t)//重载‘<’运算符,函数返回值为布尔类型(比较运算符只有是和否两种值)
    {
        return x<t.x;
    }
    node operator + (node t)//重载‘+’运算符,返回值为node类
    {
        x=x+t.x;
        return *this;//this是指向该类的指针,对其解引用*表示返回的是这个类
    }
};

点击看对this指针的解释

2.2.1.2操作符重载实现为非类的成员函数(即全局函数)

对于全局重载操作符,代表左操作数的参数必须被显式指定(即必须手动指定,用const变常量),示例如下:

struct node
{
    int x,y;
}
bool operator==(node const& p1 ,node const& p2)
{
    if (p1.x == p2.x)
    {
        return true;
    }
    else
    {
        return false;
    }
}

“操作符的重载看这里

3.标准模板库STL

3.1#include<algorithm>//推荐最后看这一块

3.1.1包括函数:

max(); min(); swap(); abs(); sort();等

3.1.2sort函数:

  • int a[10];要排此数组需要写成sort(a,a+10);//第一个参数代表首地址,第二个代表最后一个数据的后一个
  • sort默认从小到大排,如果要从大到小需要写成这种形式:sort(a,a+10,greater<int>());
  • sort自定义排序(如对struct的排序):
    1.利用c++操作符重载
    2.利用cmp函数,即第三参数,代码如下:
struct node 
{
    int x,y;
};
bool cmp(node a,node b)
{
    return a.x<b.x
};
node a[10];
sort(a,a+10,cmp);//写法是这样的,真正使用当然要放在具体的位置

3.1.3reserve()翻转

reverse(a.begin(),a.end());//翻转一个vector
reverse(a,a+n);//翻转一个下标0—n-1的数组

3.1.4 unique 去重

返回去重后的尾迭代器(指针),即去重后末尾元素的下一个位置。该函数常用于离散化,利用迭代器(指针)的减法,可计算出去重后元素的个数,当然也可以计算出重复元素的个数。

//m记录去重后元素个数,在unique函数里实现了去重并把去重后的数据存回原数组中。
int m = unique(a.begin(),a.end()) - a.begin();
int m = unique(a,a+n)-a;

3.1.5 lower_bound/upper_bound 二分查找

lower_bound的第三个参数传入一个元素x,在两个迭代器(或指针)指定的部分上执行二分查找,返回指向第一个大于等于x的元素的位置的迭代器(指针)。 upper_bound则是查找第一个大于x的元素。(当然数据一定保证有序)

3.1.6 next_permutation下一个排列

把两个迭代器(指针)指定的部分看作一个排列,求出这些元素构成全排列中,字典序排在下一个的排列,并直接在序列上更新。另外如果不存在排在更后面的排列,则返回false,否则返回true。同理有 prev_permutation函数。

//输出12345的全排列
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n=5;
    int a[100];
    for(int i=1;i<=n;i++) a[i]=i;
    while(next_permutation(a+1,a+n+1))
    {
        for(int i=1;i<=n;i++)
            cout <<a[i] <<' ';
        cout << endl;
    }
    return 0;
}

3.2 #include <queue> & #include <stack>

3.2.1包括函数:

    stack <int> s;//定义栈:stack<类型> 队列名称
    s.empty();//如果栈为空则返回true, 否则返回false;
    s.size();//返回栈中元素的个数
    s.top();//返回栈顶元素, 但不删除该元素
    s.pop();//弹出栈顶元素, 但不返回其值
    s.push();//将元素压入栈顶

    queue <int> q;//定义队列:queue<类型> 队列名称
    q.empty();//如果队列为空返回true, 否则返回false
    q.size();//返回队列中元素的个数
    q.front();//返回队首元素但不删除该元素
    q.pop();//弹出队首元素但不返回其值
    q.push();//将元素压入队列
    q.back();//返回队尾元素的值但不删除该元素

3.2.2优先队列

3.2.2.1 定义

优先队列和队列一样,只能从队尾插入元素,从队首删除元素。队列中最大的元素总是位于队首。可以通过重载<运算符来重新定义比较规则。

3.2.2.2访问

和队列不同,优先队列没有front() 和back() 函数,只能通过 top()来访问队首元素(堆顶元素,优先级最高的函数)

3.2.2.3定义优先队列
priority_queue<int> q1;
priority_queue<double> q2;
priority_queue <int,vector<int> ,greater<int> > q;//使元素按照从小到大顺序出队 
priority_queue <int,vector<int> ,less<int> > q2;//降序排列 ,无需声明vector头文件
3.2.2.4常用函数
empty() 如果队列为空,则返回真

pop() 删除队列第一个元素

push() 加入一个元素

size() 返回队列的元素个数

top() 取队顶元素//使用top()函数之前,必须用empty() 判断队列是否为空
3.2.2.5 重载运算符“<”

int,string,等类型本身可以比较大小,若使用结构体等类型或者有特殊的需求,则需要重载运算符“<”。
例如 下面的结构体保存了二维平面上的点的编号和左边,比较大小时有优先横坐标,其次纵坐标。

        struct poi
    {
        int id;
        int x,y;
    };
    bool operator < (const poi &a,const poi &b)
    {
        return a.x<b.x||a.x==b.x&&a.y<b.y;
    }

3.3#include<vector>

vector可理解为变长数组,它的内部实现基于倍增思想。按照下列思路可以大致实现一个vector:设n, m为vector的实际长度和最大长度。向vector加入元素前,若n=m,则在内存中申请2m的连续空间,并把内容转移到新的地址上(同时释放旧的空间),再执行插入。从vector中删除元素后,若n≤m/4,则释放一半的空间。
vector支持随机访问,即对于任意的下标0≤i< n,可以像数组一样用[i] 取值。但它不是链表,不支持在任意位置0(1) 插入。为了保证效率,元素的增删一般应该在末尾进行。

3.3.1声明

vector <int>a;//相当于申请一个长度动态变化的int数组
vector <int> b[100];//相当于申请一个第一维长100,第二维动态变化的int数组
struct node;
vector <node> c;//结构体类型也可以保存在vector中

3.3.2 迭代器

迭代器就像STL容器的“指针”,可以用星号“*”操作符解除引用。一个保存 int的vector的选代器声明方法为:

vector<int>::iterator it;

vector的迭代器是“随机访问迭代器”,可以把vector的迭代器与一个整数相加减,其行为和指针的移动类似。可以把vector的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离。

3.3.3 常用函数

a.size();//返回vector实际长度(元素个数)
a.empty();//vector空返回 true 非空返回false
a.clear();//清空vector
a.begin();//返回指向vector中第一个元素的迭代器,如*a.begin()等价于a[0]
a.end();//返回指向vector中最后元素后面一位的迭代器,如*a.end()等价于a[n]都是越界访问,其中n=a.size();
a.front();//返回vector第一个元素等价于a[0]
a.back();//返回vector最后一个元素 等价于*--a.end()和a[a.size()-1]
a.push_back(x);//把元素x插入vector a的末尾
a.pop_back();//删除vector末尾的一个元素

3.3.4 典型应用(vector代替邻接表保存有向图)

const int maxn = 100010;
vector <int> ver[maxn],edge[maxn];
//建边
void add(int x,int y,int z)
{
    ver[x].push_back(y);
    edge[x].push_back(z);
}
//遍历从x开始的所有边
for(int i=0; i<ver[x].size(); i++)
{
    int y=ver[x][i],z=edge[x][i];
}

3.4 include<depue>

双端队列deque是一个支持 在两端高效插入或删除元素的连续线性存储空间。它就像是vector和queue的结合。与vector相比,deque 在头部增删元素仅需要0(1)的时间;与queue相比,deque像数组-样支持随机访问。

    deque<int> dq;
    dq.begin();
    dq.end();
    dq.front();
    dq.back();
    dq.push_back();
    dq.pop_front();
    dq.pop_back();
    dq.pop_front();
    dq.clear();

3.5 include<set>

此头文件包含:set和multiset,分别为“有序集合”和“有序多重集合”,即前者的的元素不能重复,而后者可以包含若干个相等的元素。set和multiset的内部实现是一棵红黑树(平衡树的一种),它们支持的函数基本相同。

3.5.1声明

    set<int>s;
    multiset<double> s2;
    struct node;
    set<node> s3;

set和multiset存的元素必须定义“小于号”。

3.5.2迭代器

set和multiset的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号(*)解除引用,仅支持“++”和“–”两个与算术相关的操作。

set<int>::iterator it;

若把it++,则it将会指向“下一个”元素。这里的“下一个”是指在元素从小至大排序的结果中,排在it下一名的元素。同理,若把it-,则it将会指向排在“上个”的元素。
请注意,执行“++”和–”操作的时间复杂度都是0(logn)。 执行操作前后,必仔细检查,避免迭代器指向的位置超出首、尾迭代器之间的范围。

3.5.3主要函数

    set<int>s;
    s.size();
    s.empty();
    s.clear();
    s.begin();
    s.end();
    s.insert(x); //把x插入s中,s有序且不重复,时间复杂度O(logn)
    s.find(x);//s中查找x 返回迭代器,若不存在返回s.end();
    s.lower_bound(x);//返回大于等于x的元素最小的一个的返回迭代器
    s.upper_bound(x);//返回大于x的元素最小的一个的迭代器
    s.erase(it);//it为迭代器,从s中删除  it指向的元素
    s.count(x);//返回集合中等于x的个数,用于multiset

3.6 include<map>

map容器是一个键值对key-value的映射。内部实现是一棵以key为关键码的红黑树。map的key和value可以是任意类型,其中key必须定义“小于号”运算符。
声明方法:

map<key_tpye,value_type>;

很多时候map被当作Hash表使用,建立起从复杂信息key(如字符串)导简单信息value(如一定范围内的整数)的映射,大部分操作时间复杂度为O(logn)。

3.6.1迭代器

map的迭代器与set一样,也是“双向访问迭代器”。对 map的迭代器解除引用后,将得到一个二元组pair<key. type, value type>。

3.6.2 基本函数

//Insert 的参数类型是pair,erase参数可以是迭代器或者pair
    map<int,int> h;
    h.insert(make_pair(1,2));
    map<int,int> :: iterator it = h.begin();
    pair<int,int> p=*it;
    h.erase(it),h.erase(make_pair(2,3));
    //查找key为x的二元组,返回二元组的迭代器,不存在返回h.end();时间复杂度O(logn)
    h.find(x);

3.6.3 []操作符

[]操作符返回key映射到的value的引用,时间复杂度为O(logn)。
[]操作符是map最吸引人的地方,我们可以很方便地通过h[key]得key对应的value,还可以对key进行赋值操作,改变key对应的value
需要特别注意的是,若查找的key不存在,则执行h[key]后,h会自动新建一个二元组(e,zero),并返回zero的引用。这里zero表示一个广义“零值”,如整数0、空字符串等。如果查找之后不对h[key]进行赋值,那么时间一长,h会包含很多无用的“零值二元组”,白白地占用了空间,降低了程序运行效率。强烈建议读者在用[]操作符查询之前,先用find方法检查key的存在性。

3.6.4 例子

用map统计字符串出现的次数
给定n个字符串,m个问题,每个问题询问一个字符串出现的次数。n≤20000,m < 20000,每个字符串的长度都不超过20。

map<string,int> h;
    char str[25];
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str);h[str]++;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%s",str);
        if(h.find(str)==h.end()) puts("0");
        else printf("%d\n",h[str]);
    }
    return 0;

3.7 include<bitset>

bitset可看作一个多位二进制数,每8位占用1个字节,相当于采用了状态压缩的进制数想,并支持基本的位运算,在估算程序运行的时间时,我们一般以32位整数型的运算次数为基准,因此n位bitset执行一次位运算的复杂度可视为n/32,效率高。

3.7.1 声明

bitset<10000> s;
表示一个10000位的二进制数,<>里填位数

3.7.2 位运算操作符

~s:返回对s按位取反的结果
&,|,^:返回对两个位数相同的bitset执行按位与、或、异或运算的结果
“<<” “>>”:返回把一个bitset右移、左移若干位的结果
” “!=”比较两个bitset代表的二进制数是否相等

3.7.3[]操作符号

s[k]表示s的第k位,可取值,可赋值

3.7.4 基本函数

s.count();//返回有几个1
s.any();//至少一位为1返回true
s.none();//所有为0 返回true
s.set();//所有为变为1
s.set(k,v);//即k[k]=v
s.reset();//所有位变为0
s.reset(k);//即s[k]=0
s.flip();//所有位取反
s.flip(k);//即s[k]^=1;

参考资料《算法竞赛进阶指南》——李煜东著

暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇