导语:本文档整理学习中的遇到的常用语法和c++11以来的新特性,选取了一些经典的例子,用于复习和快速查询
1、STL容器&迭代器
1.1、整体介绍
c++标准模板库提供了一个容器集,针对不同的需要有不同类型的容器
分类 | 容器 |
---|---|
序列容器 | vector、list、deque |
容器适配器 | queue、stack、priority_queue |
关联式容器 | set、map、multiset、multimap |
1.1.1、顺序容器&关联容器
顺序容器:容器中的元素的顺序与其加入容器时的位置相对应
关联容器:元素按照 关键字 来保存于访问
1.1.2、 容器适配器
三个容器适配器,提供常用数据结构给开发者带来了便利:即使用容器实现数据结构中的栈、队列。默认情况下,stack和queue是基于deque实现的,priority_quque是在vector上实现的。
1.2、顺序容器
1.2.1、vector
函数编号 | 名称 | 说明 |
---|---|---|
1 | vector.push_back() &vector.emplace_back() | 在末尾加入元素 |
2 | vector.pop_back() | 删除末尾元素 |
3 | vector.empty() | 判断向量是否为空,常常与删除元素结合 |
4 | vector.size() | 获取元素个数 |
5 | vector.begin() | 获取起始元素地址 |
6 | vector.end() | 获取最后元素地址 |
7 | <algorithm>中的sort() | STL中排序函数可以实现vector的排序 |
8 | <algorithm>中的reverse() | STL中翻转函数可以实现vector的首尾翻转 |
9 | vector.front() | 获取首元素 |
10 | vector.back() | 获取尾元素 |
11 | vector.insert() & vector.emplace() | 插入元素 |
12 | vector.erase() | 删除元素 |
1.2.1.1 常规操作
代码示例如下:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
// 加入元素后打印
vector<int> v;
v.push_back(5);
v.push_back(6);
v.push_back(1);
v.push_back(23);
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
// 排序元素后打印
sort(v.begin(), v.end());
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
// 删除尾部元素打印
if(!v.empty())
v.pop_back();
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
// 加入元素(string类)后打印
vector<string> str;
str.push_back("hulk");
str.push_back("c++");
for (int i = 0; i < str.size(); i++)
cout << str[i] << " ";
cout << endl;
// 使用insert、emplace加入元素后auto打印
v.insert(v.end(),1);
v.emplace(v.begin()+1,2);
for(auto loop:v){
cout << loop <<" ";
}
cout << endl;
// 迭代器打印
vector<int>::iterator it;
for(it=v.begin(); it!=v.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
1.2.1.2 vector.resize
#include <iostream>
#include <vector>
int main()
{
std::vector<int> c = {1, 2, 3};
std::cout << "The vector holds: ";
for (const auto& el: c)
std::cout << el << ' ';
std::cout << '\n';
c.resize(5);
std::cout << "After resize up to 5: ";
for (const auto& el: c)
std::cout << el << ' ';
std::cout << '\n';
c.resize(2);
std::cout << "After resize down to 2: ";
for (const auto& el: c)
std::cout << el << ' ';
std::cout << '\n';
c.resize(6, 4);
std::cout << "After resize up to 6 (initializer = 4): ";
for (const auto& el: c)
std::cout << el << ' ';
std::cout << '\n';
}
output
The vector holds: 1 2 3
After resize up to 5: 1 2 3 0 0
After resize down to 2: 1 2
After resize up to 6 (initializer = 4): 1 2 4 4 4 4
1.2.1.3 vector.erase()
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> vec = {10, 20, 30, 40}; // 创建包含四个元素的向量
int elementToRemove = 30; // 需要删除的元素值为30
for (auto it = vec.begin(); it != vec.end(); ++it){
if (*it == elementToRemove) {
vec.erase(it); // 在迭代器指向的位置上删除该元素
break; // 只删除第一次遇到的元素,若有多个相同元素则会重复删除
}
}
cout << "删除后的向量内容:";
for (const auto& num : vec){
cout << num << " ";
}
cout << endl;
return 0;
}
output:
删除后的向量内容:10 20 40
1.2.2、list
1.3、容器适配器
1.3.1、queue
核心接口:push/front/back/pop
#include <iostream>
#include <queue>
using namespace std;
int main() {
queue<int> myQueue;
myQueue.push(1);
myQueue.push(2);
cout << myQueue.front();
myQueue.pop();
cout << myQueue.front();
return 0;
}
结果
12
1.3.2、stack
核心接口: push/pop/top
1.3.3、priority queue
#include <iostream>
#include <queue>
using namespace std;
int main() {
priority_queue<int> q;
priority_queue<int, vector<int>, greater<int> > c;
for (int n : {1,8,5,6,3,4,0,9,7,2}) {
q.push(n);
c.push(n);
}
while (!q.empty())
{
cout << q.top() << ' ';
q.pop();
}
cout << endl;
while (!c.empty())
{
cout << c.top() << ' ';
c.pop();
}
return true;
}
1.4、关联容器
关联容器支持高效的关键字查找和访问:
- 如map关键字起到索引的作用,值则是与索引相关联的数据
关联容器分为:
- 有序和无序的
无序容器:
- 无序容器不是使用比较运算符来组织元素,而是使用哈希函数和关键字类型的==运算符
- 由于无序容器不需要维护元素的序,性能会更好
1.4.1、map
函数编号 | 名称 | 说明 |
---|---|---|
1 | map.count(key) | 有该元素返回1,否则0 |
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
std::map<int, char> m;
m[11] = 'a';
m[2] = 'b';
m[3] = 'c';
using rit_t = std::map<int, char>::iterator;
for (rit_t it = m.begin(); it != m.end(); ++it) {
it->second = 'c';
}
using const_rit_t = std::map<int, char>::const_iterator;
for (const_rit_t it = m.begin(); it != m.end(); ++it) {
std::cout << "key: " << it->first << " value: "
<< it->second << std::endl;
}
std::map<string, int> v;//此时比较的是内容
const string a = "bello";
const string b = "aorld";
v[a] = 1;
v[b] = 2;
using rit_a = std::map<string, int>::iterator;
for (rit_a it = v.begin(); it != v.end(); ++it) {
std::cout << "key: " << it->first << " value: "
<< it->second << std::endl;
}
return true;
}
输入结果如下:
#include <iostream>
#include <map>
using namespace std;
int main() {
map<int, int> myMap{{1,1},{2,2},{3,3}};
auto it = myMap.insert(myMap.begin(), {4,4});
cout << it->first << ' ' << it->second << endl;
for (const auto& [key, value] : myMap) { // c++ 17
cout << '[' << key << ']' << " = " << value << endl;
}
return true;
}
output
4 4
[1] = 1
[2] = 2
[3] = 3
[4] = 4
insert(iter, pair<…>)返回一个指向插入元素的迭代器,如果map中已经存在元素,则返回的迭代器指向原先map中已经存在的元素
erase 返回下一个迭代器
2、结构体排序
struct Message {
int start;
int load;
};
vector<Message> messages;
static bool com(const Message &a,const Message &b) {
return a.start < b.start;
}
sort(messages.begin(),messages.end(),com);
3、STL算法
标准库中定义了100多个算法,大部分定义在头文件algorithm中,numeric文件中定义了一些数值泛型算法
3.1、for_each
for_each(InputIt first, InputIt last, UnaryFunction f);
- first, last:要应用函数的元素范围
- f:函数对象,要应用于解引用范围 [first, last) 中每个迭代器结果的函数
void output(int v)
{
std::cout << v << std::endl;
}
std::vector<int> nums{1, 2, 3};
std::for_each(data.begin(), data.end(), output);
3.2、 count
count(InputIt first, InputIt last, const T &value)
- first, last:要应用函数的元素范围
- value:特定元素值
3.3、count_if
count_if(InputIt first, InputIt last, UnaryPredicate p)
- first, last:要应用函数的元素范围
- p:函数对象,要应用于解引用范围 [first, last) 中每个迭代器结果的函数,返回true表示满足条件
3.4、find
find(inputIt first, inputIt last, const T &vaule)
- first, last:要应用函数的元素范围
- value:特定元素值
- 返回:指向首个满足条件的迭代器,或若找不到这种元素则为last
3.5、find_if
find_if(inputIt first, inputIt last, UnaryPredicate p)
- first, last:要应用函数的元素范围
- p:函数对象,要应用于解引用范围[first, last)中每个迭代器结果的函数,返回true表示满足条件
- 返回:指向首个满足条件的迭代器,或若找不到这种元素则为last
3.6、binary_search
- 使用二分搜索的方法查找元素,比较次数O(logN)
- 输入要求:输入序列中的元素是有序的
vector<int> haystack{1,2,3,4,5};
vector<int> needles{1,2,3};
for (auto needle : needles) {
if (binary_search(haystack.begin(), haystack.end(), needle)) {
cout << "found" << needle << endl;
}
}
3.7、all_of
all_of(inputIt first, inputlt last, UnaryPredicate p)
vector<int> data{1,2,3,4,5};
bool result = all_of(data.begin(), data.end(), [](int v){return (v > 5);}
3.8、any_of
any_of(InputIt first, InputIt last, UnaryPredicate p)
3.9、copy
copy(InputIt first, InputIt last, OutputIt d_first)
3.10 copy_if
copy_if(InputIt first, InputIt last, OutputIt d_first, UnaryPredicate p)
3.11、replace_if
replace(InputIt first, InputIt last, old_val, new_val)
3.12、sort
sort(InputIt first, InputIt last)
sort(InputIt first, InputIt last, UnaryPredicate p)
3.13、reverse
reverse(InputIt first, InputIt last);
3.14、补充
int a =0, b = 1;
auto f1=[]{return a;};//error,没有捕获外部变量
auto f2=[&]{return a++;};//ok,捕获所有外部变量,并对a执行自加运算
auto f3=[=]{return a;};//ok,捕获所有外部变量,并返回a
auto f4=[=]{return a++;};//error,a以复制方式捕获
auto f5=[a]{return a+b;};//error,没有捕获变量b
auto f6=[a,&b]{return a+(b++);};//ok,捕获a和b引用
auto f7=[=,&b]{return a+(b++);};//ok,捕获所有外部变量和b引用
4 数学库
#include <cmath>
4.1 对数函数
logm(n) = log(n)/log(m)
4.2 向上取整
ceilf(n)
5 并发编程
从C++11开始,在语言的级别引入了线程的概念,C++11 新标准中引入了几个头文件来支持多线程编程:
几个重要头文件: <condition_variable>
5.1 原子操作
所谓的原子操作,取的就是“原子是最小的、不可分割的最小个体”的意义,它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> foo(0);
void set_foo(int x) {
foo.store(x, std::memory_order_relaxed);
}
void print_foo() {
int x = 0;
do {
x = foo.load(std::memory_order_relaxed);
} while (x == 0);
std::cout << "foo:" << x << '\n';
}
int main() {
std::thread first(print_foo);
std::thread second(set_foo, 10);
first.join();
second.join();
return true;
}
5.2 互斥锁(同步)
在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西,如果不做任何处理的话,打印出来的东西肯定是错乱的。
在线程里也有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。
【互斥锁的特点】:
-
原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;
-
唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;
-
非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。
【互斥锁的操作流程如下】:
-
在访问共享资源后临界区域前,对互斥锁进行加锁;
-
在访问完成后释放互斥锁导上的锁。在访问完成后释放互斥锁导上的锁;
-
对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。
#include <iostream>
#include <thread>
#include <mutex>
#include <stdexcept>
std::mutex mtx;
void print_even(int x) {
if (x % 2 == 0) {
std::cout << x << "is even\n";
}else {
throw(std::logic_error("not even"));
}
}
void print_thread_id(int id) {
try {
std::lock_guard<std::mutex> lck(mtx);
print_even(id);
}
catch(std::logic_error&) {
std::cout << "[exception caught]\n";
}
}
int main() {
std::thread threads[10];
for (int i = 0; i<10; i++) {
threads[i] = std::thread(print_thread_id, i+1);
}
for (auto& th :threads) {
th.join();
}
return true;
}
5.3 条件变量(同步)
与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直 到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作:
一个线程等待"条件变量的条件成立"而挂起;
另一个线程使 “条件成立”(给出条件成立信号)。
【原理】:
条件的检测是在互斥锁的保护下进行的。线程在改变条件状态之前必须首先锁住互斥量。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量 可以被用来实现这两进程间的线程同步。
【条件变量的操作流程如下】:
-
初始化:init()或者pthread_cond_tcond=PTHREAD_COND_INITIALIER;属性置为NULL;
-
等待条件成立:pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真 timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait);
-
激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
-
清除条件变量:destroy;无线程等待,否则返回EBUSY清除条件变量:destroy;无线程等待,否则返回EBUSY
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
int ready = false;
void print_id(int id) {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) {
cv.wait(lck);
}
std::cout << "thread" << id << '\n';
}
void go() {
std::unique_lock<std::mutex> lck(mtx);
ready = true;
cv.notify_all();
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; i++) {
threads[i] = std::thread(print_id, i);
}
std::cout << "10 threads ready to race...\n";
go();
for(auto& th : threads) {
th.join();
}
return true;
}
6 函数指针
6.1 代码实现
#include <iostream>
using namespace std;
#define IDM_GLOBAL_REG_EXECUTE(varName_, ...) \
namespace { \
auto g_globalVarReg##varName_ __attribute__((used)) = []() { \
__VA_ARGS__; \
return 0; \
}(); \
}
#define REG_RTPC_INIT(boardTypeNameList, func) IDM_GLOBAL_REG_EXECUTE(boardTypeNameList##Init, set_func(func))
typedef void *(*rpc_service_func_t)(unsigned int func_id, void *arg1, void *arg2, void *arg3, void *arg4);
void *service_func_test(unsigned int func_id, void *arg1, void *arg2, void *arg3, void *arg4)
{
cout << *(int *)arg1 << endl;
return arg1;
}
rpc_service_func_t func_test;
void set_func(rpc_service_func_t func)
{
func_test = func;
}
void get_func(rpc_service_func_t &func)
{
func = func_test;
}
REG_RTPC_INIT(aa, service_func_test)
int main()
{
// set_func(service_func_test);
rpc_service_func_t func;
get_func(func);
int a = 5;
func(1, &a, 0, 0, 0);
}