C++学习笔记

C++学习笔记

学习视频资料

随手记

vector是一个能够存放任意类型的动态数组
unordered_set 不能放重复元素的容器 emplace放入
std::cin.get(); //使控制器不会立马关闭,保持窗口打开
总是通过 const引用传递对象
默认 动态链接
static 静态链接(只在当前文件下使用)
导入头文件,复制其中所有内容粘贴在.cpp中
sizeof(x) x占的多少字节
count = sizeof(x) / sizeof(type)
i++ :先引用后增加,先在i所在的表达式中使用i的当前值,后让i加1
++i :先增加后引用,让i先加1,然后在i所在的表达式中使用i的新值
直接传递值和引用传递:直接传递值相当于复制一下传入,原本的值保持不变;引用的话内存地址
定义一个字典: unordered_map<int, int> dic;
由于 C++ 未提供自带的链式哈希表,因此借助一个 vector 按序存储哈希表 dic 中的 key ,第二轮遍历此 vector 即可。
排序:sort(nums.begin(),nums.end());
字符串:单引号是字符型,双引号是字符串型
使用stirng的substr函数, 其中第一个参数是起始位置, 第二个参数是子串的长度 s.substr(pos, len)
replace(#include < < <string > > >)
用法一:用str替换指定字符串从起始位置pos开始长度为len的字符,string& replace (size_t pos, size_t len, const string& str);
用法二:用str替换迭代器起始位置 和 结束位置 的字符string& replace (const_iterator i1, const_iterator i2, const string& str);
用法三:用substr的指定子串(给定起始位置和长度)替换从指定位置上的字符串string& replace (size_t pos, size_t len, const string& str, size_t subpos, size_t sublen);
insert
在原串下标为pos的字符前插入字符串str
动态规划:斐波那契数列需要对中间值取模,因为中间值会太大超出内存,整数溢出

basic_string& insert (size_type pos, const basic_string& str);

str从下标为pos1开始数的n个字符插在原串下标为pos的字符前

basic_string& insert (size_type pos, const basic_string& str, size_type pos1, size_type n);

在原串下标为pos的字符前插入n个字符c

basic_string& insert (size_type pos, size_type n, char c);

“1>>n”为将二进制码向右移动n位
C/C++中**__builtin_popcount()**的使用及原理:这个函数功能:返回输入数据中,二进制中‘1’的个数

C++是如何工作的

Compile

.cpp -> .obj(机器代码文件)
属性>预处理器>预处理到文件:可以生成.i文件,查看具体生成的代码,但是不会生成.obj
属性>输出文件>汇编程序输出:可以生成.ASM文件,查看具体的硬件代码
Error: Cxxxx

Link

.obj -> .exe(可执行文件)

Error: LNKxxxx

C++变量

int - 2 31 2^{31} 231 ~ 2 31 2^{31} 231 4个bytes,每个8bit,一共32bit,其中一位代表正负
unsigned int 无符号位
char(1 byte), short, int, long, long long
float, double
bool(1 byte)
sizeof(x) 查询大小
类型* 指针 类型& 引用
在这里插入图片描述

C++函数

C++头文件

Log.cpp 存放函数
Head files 存放定义的函数签名
在main.cpp中只需要 #include “xxx.h”
“” 相对路径下的文件 <>编译器包含路径,一般调用C++标准库
例如 iosstream 没有.h 这是因为它是C++标准库,C语言的都有.h

C++条件与分支

C++循环

C++控制流语句

continue 直接进行下一次迭代
break 跳出循环
return

C++指针

指针是一个保存内存地址的整数
C++ 提供了两种指针运算符,一种是取地址运算符 &,一种是间接寻址运算符 *。
指针是一个包含了另一个变量地址的变量,您可以把一个包含了另一个变量地址的变量说成是"指向"另一个变量。变量可以是任意的数据类型,包括对象、结构或者指针。

取地址运算符 &

& 是一元运算符,返回操作数的内存地址。例如,如果 var 是一个整型变量,则 &var 是它的地址。该运算符与其他一元运算符具有相同的优先级,在运算时它是从右向左顺序进行的。
您可以把 & 运算符读作"取地址运算符",这意味着,&var 读作"var 的地址"。

间接寻址运算符 *

第二个运算符是间接寻址运算符 ∗ * ,它是& 运算符的补充。 ∗ * 是一元运算符,返回操作数所指定地址的变量的值。

int var = 8;
int* ptr = &var; //&询问内存地址
*ptr = 10 //*给内存地址赋值

C++引用

int var = 8;
int& ref = a; //引用,引用不占用内存地址

指针与引用是内存与数值之间的转化

C++类(面向对象编程)

C语言没有类,但是也可以用,类本质上只是一种grammar sugar,用它来组织代码使代码更容易维护。
class(类)默认是private
public: 可以在类之外的任何地方访问类里的变量
struct(结构体)默认是public

C++中的静态(static)

类或结构体外部使用static 只对定义它的translation unit(.cpp)可见,与类的所有实例共享内存
extern 在translation unit外部找
类或结构体内部使用static
局部静态(local static)在函数中声明局部静态变量的lifetime几乎是整个程序lifetime,scope只有函数本身
Variable lifetime and scope

C++中的枚举(ENUM,enumeration)

有一个数值集合,并且想要数字来表示它们,用枚举
必须是一个整数

enum Example
{
	A, B, C; //默认第一个是0,然后逐渐递增 
};

C++中的构造函数

在创造一个新的实例时运行
设置变量和做初始化
没有初始化的话,参数变量直接指向内存已有的东西,并不是0
当我们构造对象时,直接运行初始化代码——构造函数
对于JAVA等语言,int和float等数据基本类型会自动初始化为0,而C++不会,必须手动初始化所有基本类型

class Entity
{
public:
	float X, Y;
	
	//构造函数
	Entity()
	{
		//默认为空,在这里初始化变量
	}
};

C++中的析构函数

在销毁对象时运行
卸载变量,清理使用过的内存

class Entity
{
public:
	float X, Y;
	
	//析构函数
	~Entity()
	{
		//do something in here
	}
}

C++继承

class Entity
{
};
class Player : public Entity
{
};

C++虚函数

在子类中重写父类的function
如果想要覆写一个function,必须将基类中的基函数标记为虚函数

//基类
virtual std::string Getname() {return "xxx"; }
//子类
std::string Getname() override {return m_Name; }

内存损失:需要一个额外的内存来存储v表,使得可以分配到正确的函数,基类中要有一个成员指针,指向v表
性能损失:每次需要遍历整个v表,来确定要映射到哪个函数

纯虚函数(抽象方法/接口)

允许我们在基类中定义一个没有实现的函数,然后强制子类去实现该函数
纯虚函数必须被实现,才能创建这个类的实例

//基类
virtual std::string Getname() = 0;  //必须在子类中实现,基类无法实例化
//子类
std::string Getname() override {return m_Name; }

C++的可见性

一个面向对象编程的概念,指的是类的某些成员或方法实际上有多可见
三个基础的可见性修饰符
private:(Only*)只有这个类可以访问这些变量 friend
protected:这个类和层次结构中的所有子类,可以访问这个符号
public:所有皆可访问

C++数组

本质上是一个整型指针

int example[5]; //开辟5个int型的内存地址,在栈上创建,运行到大括号结束被destroy

//用new动态分配的内存将一直存在,直到删除它
int* another = new int[5]; //在堆上创建,直到程序把它销毁前都是active
delete[] another;

//C++11
#include <array>
std::array<int, 5> example;
count = example.size();

获取Vector容器的最后一个元素

//方法一: 
return vec.at(vec.size()-1);
 
//方法二: 
return vec.back();
//
方法三: 
return vec.end()-1;  //注意:end指向末尾元素的下一个元素。
 
//方法四: 
return vec.rbegin();

数组操作

vector<int> left; //定义一个空数组
left.push_back(nums[i]); //在数组后面append
left.insert(left.end(), right.begin(), right.end()); //两数组相加
count(left.begin(), left.end(), 'x'); //对x计数
find(left.begin(), left.end(), 'x'); //find() 函数本质上是一个模板函数,用于在指定范围内查找和目标元素值相等的第一个元素。

C++数组或vector求最大值最小值
可以用**max_element()min_element()**函数,二者返回的都是迭代器或指针。
头文件:#include
1.求数组的最大值或最小值
1)vector容器
例 vector v;
最大值:int maxValue = *max_element(v.begin(),v.end());
最小值:int minValue = *min_element(v.begin(),v.end());
2)普通数组
例 a[]={1,2,3,4,5,6};
最大值:int maxValue = *max_element(a,a+6);
最小值:int minValue = *min_element(a,a+6);
2.求数组最大值最小值对应的下标
1)vector容器
例 vector v;
最大值下标:int maxPosition = max_element(v.begin(),v.end()) - v.begin();
最小值下标:int minPosition = min_element(v.begin(),v.end()) - v.begin();
2)普通数组
例 a[]={1,2,3,4,5,6};
最大值下标:int maxPosition = max_element(a,a+6) - a;
最小值下标:int minPosition = min_element(a,a+6) - a;

注意:返回的是第一个最大(小)元素的位置。

upper_bound() 函数定义在algorithm头文件中,用于在指定范围内查找大于目标值的第一个元素。该函数的语法格式有 2 种,分别是:
//查找[first, last)区域中第一个大于 val 的元素。
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val);
//查找[first, last)区域中第一个不符合 comp 规则的元素
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
其中,first 和 last 都为正向迭代器,[first, last) 用于指定该函数的作用范围;val 用于执行目标值;comp 作用自定义查找规则,此参数可接收一个包含 2 个形参(第一个形参值始终为 val)且返回值为 bool 类型的函数,可以是普通函数,也可以是函数对象。
实际上,第一种语法格式也设定有比较规则,即使用 < 小于号比较 [first, last) 区域内某些元素和 val 的大小,直至找到一个大于 val 的元素,只不过此规则无法改变。这也意味着,如果使用第一种语法格式,则 [first,last) 范围的元素类型必须支持 < 运算符。
同时,该函数会返回一个正向迭代器,当查找成功时,迭代器指向找到的元素;反之,如果查找失败,迭代器的指向和 last 迭代器相同。
另外,由于 upper_bound() 底层实现采用的是二分查找的方式,因此该函数仅适用于“已排好序”的序列。注意,这里所说的“已排好序”,并不要求数据完全按照某个排序规则进行升序或降序排序,而仅仅要求 [first, last) 区域内所有令 element<val(或者 comp(val, element)成立的元素都位于不成立元素的前面(其中 element 为指定范围内的元素)。

accumulate定义在numeric中,作用有两个,一个是累加求和,另一个是自定义类型数据的处理
accumulate带有三个形参:头两个形参指定要累加的元素范围,第三个形参则是累加的初值。
accumulate函数将它的一个内部变量设置为指定的初始值,然后在此初值上累加输入范围内所有元素的值。accumulate算法返回累加的结果,其返回类型就是其第三个实参的类型。

C++字符串

const char* name = "Hello"; //const can't change the char in string

// string本质上只是一个char数组
#include <string> //不导入的话可以使用但是无法送到cout流中打印到控制台
std::string name = "Hello";
name.size();
name += "world";
name.find("no");
name.rfind("no"); // 反向查找

c++string函数(一)——find、rfind详细用法
字符串字面量实际上是数组或者指针

const char* name = "Hello"; //指针这个定义是只读状态,不可被修改
char name[] = "Hello"; //定义为数组,可以被修改
name[2] = 'a'; 

C++14
using namespace std::string_literals;
//字符串的append
const char* example = R"(Line1
Line2
Line3)";
const char* example = "Line1"
"Line2"
"Line3";

std::to_string()
通过std::to_string()将数字类型转换成std::string类型,从而可以直接使用+完成字符串的拼接。
stoi函数
作用是将 n 进制的字符串转化为十进制,使用时包含头文件string.
定义如下:

int stoi( const std::string& str, std::size_t* pos = nullptr, int base = 10 );

参数:
str - 待转换的字符
pos - 其取值可以是一个空字符,在这种情况下,pos未被使用;另外如果pos不是空指针,函数将pos的值设置为str中数字后面的第一个字符的位置。
base - 字符中数字的进制,默认为10进制,如果base取值为0,则进制由字符串中的格式决定。
返回值:
如果转换成功的话,stoi函数将会把转换后的得到数字以int类型返回。
如果字符串中没有数字的话,将会抛出"invalid_argument"的异常;
如果字符串中的数字转换后超过int的范围,将会抛出"out_of_range"的异常;
因此使用stoi函数的时候最好加入异常处理。

C++中的const

承诺某些东西是不变的
指针本身 or 指针指向的内容

class Entity
{
private:
	std::string m_Name;
public:
	// 1.const 不允许修改实际的类成员 如m_Name = xxx 不被允许
	// 2.const 承诺不会改变类 如在main中 const Entity e; e.Getname();如果没有这个const会报错
	const std::string& GetName() const
	{
		return m_Name;
	}
};

C++中的mutable关键字

mutable int var; //可以被改变的

类中的const方法可以修改这个成员

C++的成员初始化列表

初始化成员列表和定义的顺序应该相同
多使用

class Entity
{
public:
	float X, Y;

	//构造函数初始化成员列表
	Entity()
		: X(0.0f), Y(1.1f)
	{}
};

int main()
{	
	Entity e;
	std::cout << e.X << ' ' << e.Y << std::endl;
	std::cin.get();
}

C++的三元操作符

if语句的grammar sugar

static int s_Level = 1;
static int s_Speed = 5;

int main()
{
	if (s_Level > 5)
		s_Speed = 10;
	else
		s_Speed = 5;

	s_Speed = s_Level > 5 ? 10 : 5;

	std::string rank = s_Level > 10 ? "Master" : "Beginner";
}

创建并初始化C++对象

选择对象在栈上创建还是在堆上(new keyword)
在堆上分配对栈要花费更多时间,而且在堆上分配必须手动释放被分配的内存(delete)
JAVA所有的东西都在堆上
C#中所有类都在栈上分配

int main()
{
	Entity* e;
	{
		//在栈上分配,{}运行完在内存上销毁
		Entity entity("Hello"); 
		e = &entity; //查看地址
		std::cout << entity.GetName() <<std::endl;
	}
	std::cin.get(); //e = "Unkown"
}
int main()
{
	Entity* e;
	{
		//在堆上分配,new完在内存上delete销毁
		Entity* entity = new Entity("Hello"); 
		e = entity; //查看地址
		std::cout << (*entity).GetName() <<std::endl;
	}
	std::cin.get(); //e = "Hello"
	delete e;
}

C++ new关键字

在堆上不仅分配内存,还调用构造函数
使用new在堆上创建内存,必须使用delete释放内存
placemet new 指定内存地址

int* b = new int[];
delete[] b;

C++隐式转换与explicit关键字

explicit的构造函数,意味着没有隐式的转换

C++运算符及其重载

Vector2 Add(const Vector2& other) const
{
	return Vector2(x + other.x, y + other.y);	
}

Vector2 operator+(const Vector2& other) const
{
	return Add(other);
}

Vector2 Add(const Vector2& other) const
{
	return *this + other;
}

C++的箭头运算符

指针只是一个内存地址,是个数值,不能直接调用方法

int main()
{
	//方法一
	Entity e;
	e.Print();
	//方法二
	Entity* ptr = &e;
	Entity& entity = *ptr;  //必须逆向引用ptr
	entity.Print();
	//方法三
	Entity* ptr = &e;
	ptr->Print(); //->相当于逆向引用,从内存(pointer)中调用方法
}

还可以用于获取内存中某个值的偏移量。

C++的this关键字

访问成员函数(member function)——一个属于某一类的函数或方法,在方法内部可以引用this
this是一个指向当前对象实例的指针,该方法属于这个对象实例

// 场合一:变量与输入变量名一致,无法赋值
class Entity
{
public:
	int x, y;
	
	Entity(int x, int y)
	{
		// Entity* e = this;
		this->x = x;
		this->y = y;
	}
}
// 场合二:在一个类内部调用一个类外部的函数
class Entity
{
public:
	int x, y;
	
	Entity(int x, int y)
	{
		// Entity* e = this;
		this->x = x;
		this->y = y;
		PrintEntity(this);
	}
}

void PrintEntity(Entity* e)
{
	//do something
}

C++的对象生存期(栈作用域生存期)

基于栈的变量在一出作用域的时候就被释放了,摧毁了

C++的智能指针

自动化地在堆上创建内存(new)和释放内存(delete)
实质上是一个原始指针的包装
unique_ptr是作用域指针,在超出作用域时调用delete销毁,不能复制,因为复制后也是指向相同的内存,如果一个死亡,另一个就指向了空内存块
shared_ptr工作方式是引用计数,如果指针的引用变为0,则被销毁
shared_ptr赋值给shared_ptr会增加引用计数,但是赋值给weak_ptr不会增加

#include <memory>
int main()
{
	{
		std::unique_ptr<Entity> entity(new Entity());
		std::unique_ptr<Entity> entity = std::mask_unique<Entity>(); //如果构造函数抛出异常会安全一点

		std::shared_ptr<Entity> sharedEntity = std::mask_unique<Entity>();
	}
}

C++的复制与拷贝构造函数

strcpy includes the null termination character;

memcpy(m_Buffer, string, m_size); //复制字符串

拷贝构造函数,C++会有个默认的拷贝构造函数,做的是内存复制,将other对象的内存浅层拷贝进成员变量
浅拷贝:不会去到指针的内容或者指针指向的地方,只是复制指针,所以复制出来的指向同一内存
深拷贝:复制整个对象,不仅复制指针,也复制指针所指向的内存

String(const String& other)
	:m_Size(other.m_Size)
{
	m_Buffer = new char[m_Size + 1];
	memcpy(m_Buffer, other.m_Buffer, m_size+1); 
}

总是通过 const引用传递对象

C++动态数组

Vector - ArrayList
创建可以没有固定大小

#include <vector>

std::vector<int> list;
list.push_back(0); // Python append 在main下创建,复制进vector
list.push_back(1);
for(int i = 0; i < list.size(); i++)
	std::cout << list[i] <<std::endl;
for(int& v : list)
	std::cout << v <<std::endl;

list.clear(); //清除数组
list.erase(list.begin() + 1); //删除数组某一位

stdvector使用优化

push_back 复制旧的,然后重新分配,慢

list.reserve(3); //扩展vector
list.emplace_back(0); // 直接在vector中创建

C++中使用库(静态链接与动态链接)

OpenGL第三方库:GLFW glfw3.dll、glfw3.lib、glfw3.dll.lib
静态链接和动态链接的主要区别是库文件是否被编译到exe文件或链接到exe文件中
静态链接:这个库会放在可执行文件(.exe)中,没有外部依赖 glfw3.lib

#include <GLFW/glfw3.h>

int a = glfwInit();

动态链接:动态链接库是在运行时被链接的,可以选择在程序运行时,装载动态链接库,有外部依赖 glfw3.dll glfw3.dll.lib

引用第三方库用< >:外部的库,不在VS中和解决方案一起编译
引用第三方库用" " :会先检查相对路径,在实际解决方案中

C++中创建与使用库

C++如何处理多返回值

Array会在栈上创建,而Vector会把它的底层存储存储在堆上,所以返回std::array会更快
tuple, pair

//用结构体做
struct result
{
	std::string str;
	int val;	
};

C++的模板

模板并不是实际存在的,直到我们调用它

#include <iostream>
#include <string>

template<typename T> // 可以输出各种类型的value
void Print(T value)
{
	std::cout << value <<std::endl;
}

template<Typename T, int N> // 在栈上生成指定类型和大小的array
class Array
{
private:
	T m_Array[N];
};
Array<int, 5> array;

C++的堆与栈内存比较

int main()
{
	int value = 5; //在栈上分配内存

	int* hvalue = new int; //在堆上分配内存,new关键字分配内存
	*hvalue = 5;
}

C++的宏

预处理阶段:编译# xxxxxx
宏在预处理阶段定义一些东西,但是不要过多使用
在C++中使用宏的一些总结

#include <iostream>
#include <string>

// PR_DEBUG 在预处理器中,使得在relese版本中不会包含指定的代码(本例为打印日志)
#define PR_DEBUG 0

#if PR_DEBUG == 1
#define WAIT std::cin.get()
#define LOG(x) std::cout << x <<std::endl
#else
#define WAIT 
#define LOG(x)
#endif

int mian
{
	LOG("Hello");
	WAIT;
}

C++的auto关键字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力学习DePeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值