c++基础学习(学习蓝桥杯 ros2有C基础可看)
c++基础学习
竞赛需求
#include是一个在竞赛编程中常用的非标准头文件,它包含了C++标准库中几乎所有的标准头文件。以下详细介绍它所涵盖的内容以及相关信息:包含的主要标准库头文件
1. 输入输出流相关
<iostream>:提供了基本的输入输出流操作,例如std::cin和std::cout,用于从标准输入读取数据和向标准输出写入数据。<iomanip>:用于进行输入输出流的格式化操作,比如设置浮点数的精度、控制输出的宽度等。<fstream>:支持文件的输入输出操作,可用于创建、读取和写入文件。2. 容器相关
<vector>:动态数组容器,能够自动调整大小以容纳不同数量的元素。<list>:双向链表容器,支持高效的插入和删除操作。<deque>:双端队列容器,允许在队列的两端进行高效的插入和删除操作。<stack>:栈容器,遵循后进先出(LIFO)的原则。
<queue>:队列容器,遵循先进先出(FIFO)的原则。<priority_queue>:优先队列容器,元素按照优先级进行排序,优先级高的元素先出队。<set>:集合容器,存储唯一的元素,并且元素会自动排序。<map>:映射容器,存储键值对,键是唯一的,并且元素会根据键自动排序。<unordered_set>:无序集合容器,存储唯一的元素,但元素不会自动排序,查找效率较高。<unordered_map>:无序映射容器,存储键值对,键是唯一的,元素不会根据键排序,查找效率较高。3. 算法相关
<algorithm>:包含了大量的通用算法,如排序(std::sort)、查找(std::find)、交换(std::swap)等。4. 数值计算相关
<nath>:提供了各种数学函数,如三角函数、对数函数、幂函数等。<numeric<:包含一些数值计算的算法,如累加(std::accumulate)等。5. 字符串处理相关
<string>:用于处理字符串的类和相关操作,提供了丰富的字符串处理方法。6. 其他
<utility>:包含了一些通用的工具类和函数,如std::pair用于存储一对值。<mrmmory>:提供了内存管理相关的工具,如智能指针(std::unique_ptr、std::shared_ptr等)。
+++
'algorithm’头文件
排序算法std::sort
升序
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;int main() {vector<int>nums = {100, 15, 999, 654, 4440};//这里的排序默认从小到大sort(nums.begin(), numns.end());//这里是将nums容器中的数字一一交给num并输出for (int num : nums) {cout << num << " ";}return 0;
}
降序
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;int main() {vector<int>nums = {100, 15, 999, 654, 4440};//区别上一个就是加了一个greater<int>()sort(nums.begin(), nums.end(), greater<int>());for (int num : nums) {cout << num << " ";}return 0;
}
自己输入一些数使其排序并输出 : push_back()
- push_back() 是vector 容器的一个成员函数
- 用于在vector的末尾添加一个元素
#include <bits/stdc++.h>
using namespace std; int main() { vector<int> nums;int n;cout << "请输入你的数组的大小";cin >> n;cout << "请逐渐输入数组中的数";for(int i=0;i<n;i++){int num;cin >> num;nums.push_back(num); //}sort(nums.begin(),nums.end());for(int num:nums){cout << num<<" ";}return 0;
} //输出
请输入你的数组的大小5
请逐渐输入数组中的数12 10 56 8 60
8 10 12 56 60
看数组的大小(相当于c中的malloc)
//上一组代码的基础上
cout<<"数组的大小是";cout << nums.size();
查找算法find
#include <bits/stdc++.h>
using namespace std; int main() { vector<string> zhao{"apple","banana","pink"}; //这里注意因为数组和中的是多字母的 所以不可以是char 类型 只可以是string 类型auto it = find(zhao.begin(),zhao.end(),"pink");//这里的it 是一个迭代器 指向目标元素 //zhao.end 是表示超出最后一个元素的位置if(it!=zhao.end()){ cout << "Found!!" << *it << endl;}else {cout << "not here";}return 0;
} //输出
found!!pink
根据你输入的值去找
#include <bits/stdc++.h>
using namespace std; int main() { vector<string> zhao{"apple","banana","hahah"};cout << "请输入你想找的水果";string t;cin >> t;auto it = find(zhao.begin(),zhao.end(),t);if(it!=zhao.end()){cout << "Found!!" << *it << endl;}else {cout << "not here";}return 0;
} //输出
请输入你想找的水果hahah
Found!!hahah
交换算法swap
#include <bits/stdc++.h>
using namespace std; int main() { int a=10,b=20;swap(a,b);cout << "a=" << a << "b=" << b << endl;return 0;
} //输出
a=20b=10
+++
与c的区别和联系
一、C语言与C++的关键字概览
1. C语言关键字
C语言共有 32个关键字,主要包括基础数据类型、流程控制、存储类别等,例如:
- 数据类型:
int,char,float,double,void- 流程控制:
if,else,for,while,switch,break,continue,return- 存储类别:
auto,static,extern,register- 其他:
sizeof,typedef,struct,union,enum
462. C++关键字
C++在C语言基础上扩展了大量关键字,C++98/03标准中共有63个关键字,新增的关键字主要支持面向对象、泛型编程、异常处理等特性,例如:
- 面向对象:
class,public,private,protected,virtual,friend,this- 类型扩展:
bool,wchar_t,mutable,typename- 异常处理:
try,catch,throw- 泛型编程:
template,typename
- extern : 这里直接在全局 各个文件中直接定义了一个变量 避免了每个文件都定义一个变量从而浪费内存的状况
- signed : 可以存储正数,负数的情况 但是**unsigned **只可以存储正数的
+++
简单例子
(这里为了速学 因为有C的基础 所以直接引用一段代码进行学习)
#include <iostream>
using namespace std;int main() {int i = 0; // 初始化循环变量 while (i < 5) { // 条件判断:当 i < 5 时执行循环 cout << "当前值:" << i << endl;i++; // 更新循环变量,避免死循环 }return 0;
}
using namespace std
引入标准命名空间
C++标准库中的所有标识符(如cout、cin、endl)都被封装在名为std的命名空间中。使用using namespace std;后,程序可以直接访问这些标识符,而无需显式添加std::前缀
举例:前者需要为每个标准库成员添加 std::,后者通过声明命名空间省略前缀 更加的方便了
// 不使用 using namespace std;
std::cout << "Hello World" << std::endl;// 使用 using namespace std;
cout << "Hello World" << endl;
-
cout- 作用:C++标准输出流对象,用于向控制台(如终端或命令行窗口)输出内容
- 依赖命名空间:若未声明
using namespace std;,需写为std::cout - 这里如果写cout << i <<" " ; (i是一个从1到5的循环) 那么
输出的顺序是先输出1 , 再输出空格 , 再输出2 …
-
<<运算符- 流插入操作:将右侧的数据(字符串、变量值等)传递到左侧的输出流中,支持链式调用
- 示例:
cout << a << b;等效于依次输出a和b。
-
endl
换行并刷新缓冲区
-
插入换行符(
\n),使后续输出从新行开始。 -
强制刷新输出缓冲区,确保内容立即显示(避免延迟输出)4。
-
替代方案:使用
'\n'仅换行但不刷新缓冲区(性能更高)。
+++
换行符号
endl 与 \n
| 操作 | 换行 | 刷新缓冲区 | 性能影响 |
|---|---|---|---|
endl | 是 | 是 | 较高(频繁刷新时) |
\n | 是 | 否 | 较低 |
cout << "Pass\n"; // 仅换行,不刷新缓冲区
cout << "Pass" << endl; // 换行并刷新缓冲区
+++
输入输出
输入 >> 输出 <<
#include <iostream>
using namespace std;int main() {{int score;cout << "请输入分数:"; // 提示用户输入 cin >> score; // 读取输入值到score变量 if (score <= 20) {{cout << score << endl;}} else if (score >= 90) {{cout << "niubi" << endl;}} else {{cout << "Failed" << endl;}}return 0;
}}
+++
语法结构
循环
for
#include <iostream>
using namespace std;int main(){for(int i=0;i<=5;i++){cout << i<<" ";}return 0;
}/* 输出
0 1 2 3 4 5
*/
//或者
#include <iostream>
using namespace std;int main() {int arr[] = {1, 2, 3, 4, 5};for(int num : arr) {cout << num << " "; // 打印 num 本身,而不是 arr[num]}return 0;
}
while
#include <iostream>
using namespace std;int main() {int x=3;while (x>0){cout << x << " ";x--;}return 0;
}//输出 3 2 1
do while
#include <iostream>
using namespace std;int main() {int x = 3;do{cout << "先执行一次再说" <<endl ;}while (x<0);return 0;
}//输出: 先执行一次再说
选择
if else
#include <iostream>
using namespace std;int main() {{int num = 10;if(num>0){cout << "正数" << endl;}else if (num<0){cout << "负数" <<endl;}else{cout<< "零"<<endl;}return 0;
}}//输出
正数
switch
#include <iostream>
using namespace std;int main(){enum Color { Red , Green, Blue};Color c = Green;switch(c){case Red: cout << "Red" << endl; break;case Green: cout << "Green" << endl; break;case Blue: cout << "Blue" <<endl; break;default: cout <<"Unknow"<<endl;}return 0;
}// 输出 Green
case 后的常量必须是整型或字符型(和 C 语言一致)
预处理命令
- #include 命令
ps:
#include <文件名>
#include "文件名"
- 这里如果是
<>的文件 则预处理会在系统指定的标准库目录中寻找头文件 常用于包含标准库的头文件 ""预处理会在当前源文件所在的目录中查找头文件 如果找不到 则再到系统目录的标准库中查找 常用于包含自定义的头文件
- #ifdef , #ifndef , # endif命令
#ifdef:检查某个宏是否已定义,若定义则编译后续代码。#ifndef:检查某个宏是否未定义,若未定义则编译后续代码。#endif:结束条件编译块(必须成对使用)
- #undef命令
- 用于取消之前定义的宏
#define PI 3.14159
#include <iostream>
#undef PI // 取消 PI 宏的定义
int main() { // 此时 PI 不再是 3.14159,下面这行代码会编译错误 // double area = PI * 2 * 2; return 0;
}
- #program命令
- 它提供了一些于编译器实现相关的功能 不同的编译器对#program的支持可能不同 常见的用途包括设置编译选项 控制内存对齐等
#pragma once // 一些编译器支持的命令,作用是确保头文件只被包含一次
#include <iostream>
int main() { std::cout << "Hello, World!" << std::endl; return 0;
}
类
命名空间
:: 空间限定操作符
对于变量,函数,类之前加上“命名空间:: ”就可以限定空间了
std::cout << "hello" <<endl// 我们经常看到c++中有这样的写法 就是使用了std中的coutusing namespace std; //这就是使用了一个命名空间名叫std,接下来使用的函数,类之类的如不特定说明都是std这个命名空间里面的
cout << "hello" <<endl; 那么这里cout 就是用的是std里面的 using namespace std;
using namespace my_namespace;int main() {cout << "Hello" << endl; // 来自 stdmy_namespace::func(); // 来自 my_namespace ,这里要用my_space中的函数就要特定说明一下了return 0;
} //命名空间的合并性
namespace dj{int a=0;
}
namespace dj{int add(int a,int b){return a+b;}
}int main(){int a=1;std::cout <<dj::a << "" <<dj::add(1,2)<< std::endl}//命名空间的嵌套
namespace dgj{namespace a{int add(int a=0,int b=1){return a+b+1;
}
namespace b{int add(int a,int b){return a+b;}}
}
using namespace dgj::b //使用命名空间bgj中的命名空间b
默认成员函数
1.构造函数2.析构函数 ~Box() {std::cout << "析构函数时用\n";}
作用:对象生命周期结束时自动执行,用来清理资源(比如关闭文件、释放内存)
classs Box{
public:
Box() {std::cout << "构造函数时用\n";}
~Box() {std::cout << "析构函数时用\n";}}3.拷贝构造函数 Box(const Box&other)
作用:用一个已有对象来初始化另一个对象class Box{
public:int value;Box(int v) : value(v){std::cout<<"构造函数调用,value=" <<value ''}
}
Box b(10);4.拷贝赋值运算符 Box& operator=(const Box& other)
static
- 静态成员变量 所有人共用一个 必须在类内才可以
static int totalCount;
- 静态局部变量 局部才可以使用 但是下一次调用函数时是在上一次的基础上加的
void demoStaticLocalVar() {static int count = 0; // 静态局部变量count++;std::cout << "函数被调用了 " << count << " 次\n";
}
- 静态成员函数 不依赖具体对象,可以用类名调用
Box::showTotalCount();
隐式类型转换 匿名对象
#include <iostream>class Box {
public:int value;Box(int v) : value(v) {std::cout << "构造函数: value=" << value << "\n";}~Box() {std::cout << "析构函数: value=" << value << "\n";}
};void printBox(Box b){std::cout << "box.value是" << n.value ;}//1. 隐式类型转换
int main{
printBox(10); //这里正常是printBox(Box temp(10)) 但直接省略了Box类型参数
}//2. 匿名对象
int main() {printBox(Box(10)); // ✅ 匿名对象 Box(10)// 没有写成 Box temp(10);// 用完立刻销毁
}//3.如果不想隐式
class Box {
public:int value;explicit Box(int v) : value(v) {cout << "构造函数: value=" << value << endl;}
};
printBox(10); // ❌ 编译错误:不允许隐式类型转换
printBox(Box(10)); // ✅ 必须手动写出来
智能指针
- 智能指针的使用要引用文件**#Include**
new
动态内存分配的操作符
并返回指向该内存的指针
int *p = new int; //给p分配了一个int大小的内存
释放:
delete p;
若忘记释放会造成内存泄漏int* p = new int(5); //分配并初始化为 5int* arr = new int[10]; //分配 10 个 int 的连续内存。返回指向第一个元素的指针。
释放:
delete[] arr;MyClass* obj = new MyClass(参数); //调用构造函数分配对象。
下面是对比自己动手删除和智能指针方式方式的代码
#include <iostream>
#include <memory> // for unique_ptrint main() {// 基本 newint* basic = new int(10);std::cout << "基本 new: " << *basic << std::endl;delete basic;// 数组 newint* arr = new int[3]{1, 2, 3};for (int i = 0; i < 3; ++i) {std::cout << arr[i] << " "; // 1 2 3}std::cout << std::endl;delete[] arr;// 智能指针 newauto smart = std::make_unique<int>(20); // C++14+ 推荐,内部用 newstd::cout << "智能指针: " << *smart << std::endl; // 20,自动 deletereturn 0;
}//输出
基本 new: 10
1 2 3
智能指针: 20
unique_ptr
- unique_ptr没有复制构造函数,不支持普通的拷贝和赋值操作。因为unique_ptr独享被管理对象指针所有权
ps:
void f1() {unique_ptr<int> p(new int(5));cout<<*p<<endl;unique_ptr<int> p2(p);unique_ptr<int> p3 = p;
}对于p2 p3 会报错
无法引用 函数 "std::__1::unique_ptr<_Tp, _Dp>::unique_ptr(const std::__1::unique_ptr<int, std::__1::default_delete<int>> &) [其中 _Tp=int, _Dp=std::__1::default_delete<int>]" (已隐式声明) -- 它是已删除的函数
- unique_ptr虽然不支持普通的拷贝和赋值操作,但却可以将所有权进行转移,使用std::move方法即可
void f1() {unique_ptr<int> p(new int(5));unique_ptr<int> p2 = std::move(p);//error,此时p指针为空: cout<<*p<<endl; cout<<*p2<<endl;
}//输出 5
以及对于类的代替使用
class objtype{
pubic: void func() { // 成员函数std::cout << "func() 被调用了!" << std::endl;}~MyClass() { /std::cout << "对象被销毁" << std::endl;}
}unique_ptr<objtype> p(new objtype()); //创建了一个类是p
p -> func(); p可以调用类里面的函数
delete p
shared_ptr
- 可以进行赋值拷贝
void f(){shared_ptr<int> p = make_shared<int>(1);int *p2 = p.get();cout <<*p2 << endl; //可以获得p里指针代表的值shared_ptr<int> p2(p);shared_ptr<int> p3 = p; //这两个方法赋值都是可以的
}
shared_ptr需要注意的点:
- 不能将一个原始指针初始化多个shared_ptr , 因为p1,p2都要进行析构删除 这样会导致原始指针p0被删除两次
void f2() {int *p0 = new int(1);shared_ptr<int> p1(p0);shared_ptr<int> p2(p0);cout<<*p1<<endl;
}
-
shared_ptr最大的坑就是循环引用
该部分代码会有内存泄漏问题。原因是
1.main 函数退出之前,Father 和 Son 对象的引用计数都是 2。
2.son 指针销毁,这时 Son 对象的引用计数是 1。
3.father 指针销毁,这时 Father 对象的引用计数是 1。
4.由于 Father 对象和 Son 对象的引用计数都是 1,这两个对象都不会被销毁,从而发生内存泄露。为避免循环引用导致的内存泄露,就需要使用 weak_ptr。weak_ptr 并不拥有其指向的对象,也就是说,让 weak_ptr 指向 shared_ptr 所指向对象,对象的引用计数并不会增加
struct Father
{shared_ptr<Son> son_;
};struct Son
{shared_ptr<Father> father_;
};int main()
{auto father = make_shared<Father>();auto son = make_shared<Son>();father->son_ = son;son->father_ = father;return 0;
}
改进:
struct Father
{shared_ptr<Son> son_;
};struct Son
{weak_ptr<Father> father_;
};int main()
{auto father = make_shared<Father>();auto son = make_shared<Son>();father->son_ = son;son->father_ = father;return 0;
}
map
- 包含头文件**#include
- 标准模板库(STL)中的关联容器,以键值对(key-value pair)形式存储数据
构造函数
map<string,int>mapstring;
map<int ,string >mapint;
map<sring, char>mapstring;
map< char ,string>mapchar;
map<char ,int>mapchar;
map<int ,char >mapint;
增加数据
- 以记录学生成绩为例子
方法1:以数组下标的形式直接增加,即:变量名[key] = value 的形式。
#include <iostream>
#include <map>
#include <string>
using namespace std;int main() {map<string, int>node;node["张三"] = 90;node["李四"] = 100cout << "张三成绩为:" << node["张三"] << endl;cout << "李四成绩为: " << node["李四"] << endl;}//输出
张三成绩为:90
李四成绩为: 100
方法二:
#include <iostream>
#include <map> // 头文件
#include <string>
using namespace std;int main() {map<string, int>node;node.insert(pair<string, int>("张三", 90));node.insert(pair<string, int>("李四", 60));cout << "张三成绩为:" << node["张三"] << endl;cout << "李四成绩为: " << node["李四"] << endl;}//输出
张三成绩为:90
李四成绩为: 60
删除数据
#include <iostream>
#include <map> // 头文件
#include <string>
using namespace std;int main() {map<string, int>node;node.insert(pair<string, int>("张三", 90));node.insert(pair<string, int>("李四", 60));node["王五"] = 100;cout << "size = " << node.size() << endl;//使用Key删除node.erase("张三");cout << "size = " << node.size() << endl;//使用迭代器删除map<string, int>::iterator iter = node.find("李四");node.erase(iter);cout << "size = " << node.size() << endl;//清空整个容器node.clear();cout << "size = " << node.size() << endl;}//输出
size = 3
size = 2
size = 1
size = 0
查找数据
#include <iostream>
#include <map> // 头文件
#include <string>
using namespace std;int main()
{map<int, string>node; // 定义变量node[123456] = "张三";node[123457] = "李四";node[123458] = "王五";map<int, string>::iterator iter = node.find(123456);if(iter != node.end()) {cout<<"身份证号123456的人叫"<<iter->second<<endl;//这里的second是map里int string 的第二个也就是string}
} //输出
身份证号123456的人叫张三
便利元素
#include <iostream>
#include <map> // 头文件
#include <string>
using namespace std;int main() {map<int, string>node; // 定义变量node[123456] = "张三";node[123457] = "李四";node[123458] = "王五";map<int, string>::iterator iter;for (iter = node.begin(); iter != node.end(); iter++) {cout << "身份证号:" << iter->first << "姓名:" << iter->second << endl;}
}//输出
身份证号:123456姓名:张三
身份证号:123457姓名:李四
身份证号:123458姓名:王五
Lambda
- 简单点说 Lambda就是可以捕获一些数据然后进行操作,不需要专门建立函数或者类什么的
- 通常和STL里的sort排序 便利for_each,多线程等配合使用
- 结构
[capture](parameter) mutable->return_type{body}
[capture]:捕获列表。指定 Lambda 能访问外部变量的方式(详见下节)。
(parameters):参数列表。可以为空 (),类似于普通函数的参数。
mutable:可选关键字。如果指定,允许 Lambda 修改捕获的变量(默认是 const 的)。
-> return_type:可选的返回类型推导。如果省略,编译器会自动推导(C++11+ 支持)。
{ body }:函数体,包含 Lambda 的逻辑代码。
Capture 捕获列表
三种捕获方式
- 按值捕获[=] , 复制外部变量到Lambda中 ,只读形式 不可修改
- 按引用捕获[&] ,通过引用访问外部变量,可修改
- 显示捕获:指定特定变量,如 [&x, y](x 按引用,y 按值)或 [this](捕获 this 指针)
#include <iostream>
#include <string>
using namespace std;int main() {int x = 9, y = 10;auto lambda = [x, &y]() {return x + y;};int result = lambda();cout << result << endl;return 0;
}//通过capture& 改变外部变量
int x = 10, y = 20;
auto add = [x, &y]() { y += x; return x + y; }; // x 按值,y 按引用
int sum = add(); // sum = 30, y 现在是 30
返回类型和mutable
如果使用mutable 就可以改变副本(val)的值 但是外部真正的(val)值不变
#include <iostream>
#include <string>
using namespace std;int main() {int val = 5;auto increment = [&val]() {return ++val;}; // mutable 允许修改 val 的副本int new_val = increment(); // new_val = 6, 外部 val 仍为 5cout << new_val << endl;return 0;
}
实操
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;int main() {std::vector<int> arrs = {89, 16, 46, 50, 69, 96};sort(arrs.begin(), arrs.end(), [](int a, int b) {return a > b;});for (int arr : arrs) {cout << arr << " " << endl;}return 0;
}
