launch.json "program": "${fileDirname}/${fileBasenameNoExtension}" ,
#include
这是一个预处理命令,在调用编译器运行时,该指令使得预处理器将include后面的<>中的文件读入程序,如同将这个文件输入到源代码中的这个位置
#define是一个预处理器宏,预处理器将进行文本替换,而不是智能替换
#include <cstdint> 使用整型数据
#include<cmatch> 使用常量和常量表达式
#include<iostream>
using namespace std; 使用#include<iostream>时,要有using namespace std;
#include<vector> 使用动态数组
#include<string> 使用字符串
#include<bitset> 使用二进制数
#define编写宏函数
assert 验证表达式(也常做函数使用)
常量:const const double pi=22.0/7
常量表达式:constexpr constexpr double Getpi() {return 22.0/7}
:: 域解析运算符,作用域运算符(表明归属性)
逻辑运算符NOT(!) AND(&&) OR(||)
按位运算符NOT(~) AND(&) OR(|) XOR(^)
移位运算符 >> << 不会旋转,左移后右边补零
enum枚举
Public 类属性和声明为公有的,有了对象就可以获取
Private 声明为私有的,只能在类的内部或其友元中访问
二分模板:
当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1,计算mid时不需要加1,即mid = (l + r)/2。
当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid,此时为了防止死循环,计算mid时需要加1,即mid = ( l + r + 1 ) /2。
*使用时根据代码进行判断,看令L=mid OR mid+1,L=mid+1,则mid=(l+r)/2.
程序=数据结构+算法
数据结构:对数据的存储方式(指数据类型)
算法:对存储好的数据进行分析的步骤,操作数据的步骤=功能函数
c++的三大特性:
1.封装:把客观的事物封装成抽象的类(将数据和方法打包在一起,加以权限的区分,达到保护并安全使用数据的目的)
意义:将属性和行为作为一个整体表现生活中的事物;将属性和行为加以权限控制
Struct和class的区别在于默认的访问权限不同:struct默认公共,class默认私有
Class类中的属性和行为统称为成员:
属性/成员属性/成员变量 行为/成员函数/成员方法
成员变量和成员函数分开储存,非静态成员变量属于类的对象上,静态成员变量、非静态成员函数、静态成员函数不属于类的对象上
访问权限:
公共权限 public 成员 类内可以访问,类外可以访问
保护权限 protect 成员 类内可以访问,类外不可以访问
私有权限 private 成员 类内可以访问,类外不可以访问
友元friend,可访问类的私有属性,实现方法:
- 全局函数做友元 将全局函数加关键字friend放在类下面表示声明即可使用
- 类做友元 将一个类的名称加关键字friend放在另一个类下面表声明
- 成员函数做友元 将一个成员函数用类外表达的方式在一个类中加关键字friend
- 继承:表达的是类之间相关的关系,使得对象可以继承另一类对象的特征和能力(避免公用的代码重复开发,减少代码和数据冗余)
Class 子类 : 继承方式 父类 (子类/派生类;父类/基类)
继承方式有:公共继承(继承后公共的仍为公共的);保护继承(继承后公共的变为保护的);私有继承(继承后公共和保护的都变为私有的)—任何方式都不能访问私有的,只是不能访问,被继承下去了,会占有内存
访问子类同名成员,直接访问即可;访问父类同名成员,需要加作用域;当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
菱形继承(钻石继承):
两个派生类继承同一个基类;又有某个类同时继承这两个派生类——在继承之前加上关键字virtual变为虚继承,此时的基类为虚基类,vbptr:虚基类指针,会指向一个虚基类表格vbtable
- 多态:“一个接口,多种方法”
在不同继承关系的类对象,去调同一函数,产生了不同的行为
静态多态和动态多态:
静态多态函数地址早绑定—编译阶段确定函数地址
动态多态函数地址晚绑定—运行阶段确定函数地址
动态多态满足条件:(1)有继承关系(2)子类重写父类的虚函数
动态多态使用:父类的指针或引用,指向子类对象
Sizeof 可求出数据类型占内存大小以及数据大小(内存大小) sizeof(int)
转义字符:
\a警报 \b退格,将当前位置移到前一列 \f换页符
\n换行符号 \t水平制表符,跳到下一个TAB位置
rand可生成伪随机数,rand()%100即生成0-99,rand()%100+1即生成1-100
为了生成真正随机数,可添加随机种子数srand(unsigned int)time(NULL))
冒泡排序:
- 比较相邻的元素,如果第一个比第二个大,就交换他们两个
- 对每一队相邻元素做同样的工作,执行完毕后,找到第一个最大值
- 重复以上的步骤,每次比较次数-1,直到不需要比较
排序总轮数=元素个数-1;每轮对比数=元素个数-排序轮数-1;
// 利用冒泡实现对[4,2,8,0,5,7,1,3,9]的排序
int arr[9]={4,2,8,0,5,7,1,3,9};
for(int i=0;i<9-1;i++)
{
for(int j=0;j<9-i-1;j++)
{
if(arr[j]>arr[j+1])
{
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
for(int i=0;i<9; i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
函数的分文件编写:
作用:让代码结构更清晰
- 创建后缀为.h的头文件
- 创建后缀为.cpp的源文件
- 在头文件中写函数的声明
- 在源文件中写函数的定义
函数赋值时如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值;函数声明和函数实现只能最多一个含有默认参数
函数重载:
作用:函数名可以相同,提高复用性
函数重载存在需满足条件:
- 同一个作用域下
- 函数名称相同
- 函数参数类型不同或者个数不同或者顺序不同
运算符重载:
对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
对于左移运算符,一般只使用全局函数进行重载;
区分前置++/--和后置++/--用int做占位符进行区分,前置递增返回引用,后置返回值
构造函数和析构函数:
构造函数和析构函数都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构
构造函数:创建对象时为对象的成员属性赋值
- 没有返回值也不写void
- 函数名称与类名相同
- 可以有参数,因此可以发生重载
- 程序在调用对象时会自动调用构造,无需手动调用,且只会调用一次
析构函数:在对象销毁前系统自动调用,执行一些清理工作
- 没有返回值也不写void
- 函数名称与类名称相同,在名称前加符号~
- 不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用析构,无需手动调用且只会调用一次
拷贝构造函数调用时机:
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 值方式返回函数对象
深拷贝与浅拷贝:
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
const修饰指针:
- const修饰指针——常量指针 const int *p=10,指针指向的常量不可改,指向可以改
- const修饰常量——指针常量 int * const p=&a,指向不可以改,指向的值可以改
- const既修饰指针,又修饰常量 const int * const p=&a,指向和指向的值都不可改
&和*
一个指针占四个字节空间
#include<iostream>
using namespace std;
int main(){
int a=123;
//&a表示a在内存中的地址,也就是123在内存中的地址 &a=0019ff3c;
cout<<"a: "<<a<<endl<<"a's address:"<<&a<<endl;
//此时p是一个指针,指向a所在的位置 p=0019ff3c;
int *p=&a;
cout<<"p: "<<p<<endl;
//声明p之后,在p之前添加*表示p指向内存的值 *p=123
cout<<"p's value: "<<*p<<endl;
//p是一个变量,有一个地址储存它,但其地址不是a的地址 &p=0019ff38
cout<<"p's address: "<<&p<<endl;
//试试*&组合使用是什么效果 *&p=0019ff3c
cout<<"*&p: "<<*&p<<endl;
//&p是一个内存地址,*&p表示&p指向地址内存空间的值 **&=123
cout<<"**&p: "<<**&p<<endl;
//刚才我们已经知道*&p是a的地址,那么**&p就表示a的值
return 0;}
const修饰成员函数:
常函数:
- 成员函数后加const我们称这个函数为常函数,加const修饰的是this指向,让指针指向的值也不可以修改
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
this指针:
this指针:指向被调用的成员函数所属的对象;是隐含每一个非静态成员函数内的一种指针;不需要定义,直接使用即可;本质是指针常量,指向不可修改
用途:
- 当形参和成员变量同名时,可用this指针区分
- 在类的非静态成员函数中返回对象本身,可使用 return *this
结构体
通过结构体创建变量的方式:
- struct 结构体名 变量名
- Struct 结构体名 变量名={成员1值,成员2值。。。}
- 定义结构体时顺便创建变量
结构体指针可以通过->操作符来访问结构体中的成员
结构体中可含有另一个结构体
内存分区模型:
c++程序在执行时,将内存大方向划分为4个区域:
- 代码区:存放函数体的二进制代码
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收 利用new堆区可以将数据开辟到堆区,用delete可以释放,释放数组时加[] 指针本质也是局部变量,放在栈上,指针保存的数据是放在堆区 new返回的是该数据类型的指针
文件操作:
c++中对文件操作需包含头文件<fstream>
文件类型分为:
- 文本文件:文件以文本的ASCII码形式存储在计算机中
写文件步骤:
- 包含头文件 #inculde <fstream>
- 创建流对象 ofstream ofs;
- 打开文件 ofs.open(“文件路径”,打开方式)
- 写数据 ofs<<”写入的数据”
- 关闭文件 ofs.close();
读文件步骤:
(1)包含头文件 #inculde <fstream>
(2)创建流对象 ifstream ifs;
(3)打开文件并判断文件是否打开成功 ifs.open(“文件路径”,打开方式)
(4)读数据 四种方式读取
(5)关闭文件 ifs.close();
操作文件的三大类:
- ofstream:写操作
- ifstream:读操作
- fstream:读写操作
打开方式 | 解释 |
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
函数模板:
作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表;
语法:template<typename T> (template—声明创建模板;typename—表明其后面的符号是一种数据类型,可以用class代替;T—通用数据类型,可替换,通常为大写字母)
STL
基本概念:
Standard template library标准模板库;分为容器、算法、迭代器;容器和算法之间通过迭代器进行无缝衔接;几乎所有代码都采用了模板类或者模板函数
六大组件:
- 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
- 算法:各种常用算法,如sort、find、copy、for_each等
质变算法:指运算过程中会更改区间内的元素的内容,如拷贝、替换、删除等
非质变算法:运算过程中不会更改区间内的元素内容,如查找、计数、遍历、寻找极值
- 迭代器:扮演了容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:用来修饰容器或者仿函数或迭代器接口的东西
- 空间配置器:负责空间的配置和管理
String
String和char*区别:
- char*是一个指针
- String是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器
Vector
Vector数据结构和数组非常相似,也成为单端数组,可动态扩展
栈区stack memory
堆区 dynamic memory—用new创建的变量都在堆上,不用new创建的变量都在栈上
typedef用来定义一个别名的 格式是:typedef <原名> <类型别名>
执行浮点运算选用double,float通常精度不够而且双精度浮点数和单精度浮点数的计算代价相差无几,一般float类型为32个二进制位,double类型为64个二进制位
数据结构:是计算机存储、组织数据的方式;算法是为了解决实际问题而设计的,数据结构是算法需要处理的问题的载体
线性表:是零个或多个数据元素的有限序列;特性:数据元素之间是有顺序的、数据元素个数是有限的、数据元素的类型必须相同