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
升序
c++
#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;
}
降序
c++
#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的末尾添加一个元素
c++
#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)
c++
//上一组代码的基础上
cout<<"数组的大小是";
cout << nums.size();
查找算法find
c++
#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
根据你输入的值去找
c++
#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
c++
#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的基础 所以直接引用一段代码进行学习)
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::
,后者通过声明命名空间省略前缀 更加的方便了
c++
// 不使用 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 |
是 | 否 | 较低 |
c++
cout << "Pass\n"; // 仅换行,不刷新缓冲区
cout << "Pass" << endl; // 换行并刷新缓冲区
+++
输入输出
输入 >>
输出 <<
c++
#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
c++
#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
c++
#include <iostream>
using namespace std;
int main() {
int x=3;
while (x>0){
cout << x << " ";
x--;
}
return 0;
}
//输出 3 2 1
do while
c++
#include <iostream>
using namespace std;
int main() {
int x = 3;
do{
cout << "先执行一次再说" <<endl ;
}while (x<0);
return 0;
}
//输出: 先执行一次再说
选择
if else
c++
#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
c++
#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:
c++
#include <文件名>
#include "文件名"
- 这里如果是
<>
的文件 则预处理会在系统指定的标准库目录中寻找头文件 常用于包含标准库的头文件 ""
预处理会在当前源文件所在的目录中查找头文件 如果找不到 则再到系统目录的标准库中查找 常用于包含自定义的头文件
- #ifdef , #ifndef , # endif命令
#ifdef
:检查某个宏是否已定义,若定义则编译后续代码。#ifndef
:检查某个宏是否未定义,若未定义则编译后续代码。#endif
:结束条件编译块(必须成对使用)
- #undef命令
- 用于取消之前定义的宏
c++
#define PI 3.14159
#include <iostream>
#undef PI // 取消 PI 宏的定义
int main() {
// 此时 PI 不再是 3.14159,下面这行代码会编译错误
// double area = PI * 2 * 2;
return 0;
}
- #program命令
- 它提供了一些于编译器实现相关的功能 不同的编译器对#program的支持可能不同 常见的用途包括设置编译选项 控制内存对齐等
c++
#pragma once // 一些编译器支持的命令,作用是确保头文件只被包含一次
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
类
命名空间
:: 空间限定操作符
对于变量,函数,类之前加上"命名空间:: "就可以限定空间了
std::cout << "hello" <<endl// 我们经常看到c++中有这样的写法 就是使用了std中的cout
using namespace std; //这就是使用了一个命名空间名叫std,接下来使用的函数,类之类的如不特定说明都是std这个命名空间里面的
cout << "hello" <<endl; 那么这里cout 就是用的是std里面的
using namespace std;
using namespace my_namespace;
int main() {
cout << "Hello" << endl; // 来自 std
my_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); //分配并初始化为 5
int* arr = new int[10]; //分配 10 个 int 的连续内存。返回指向第一个元素的指针。
释放:
delete[] arr;
MyClass* obj = new MyClass(参数); //调用构造函数分配对象。
下面是对比自己动手删除和智能指针方式方式的代码
#include <iostream>
#include <memory> // for unique_ptr
int main() {
// 基本 new
int* basic = new int(10);
std::cout << "基本 new: " << *basic << std::endl;
delete basic;
// 数组 new
int* 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;
// 智能指针 new
auto smart = std::make_unique<int>(20); // C++14+ 推荐,内部用 new
std::cout << "智能指针: " << *smart << std::endl; // 20,自动 delete
return 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["李四"] = 100
cout << "张三成绩为:" << 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 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 仍为 5
cout << 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;
}