C++的lambda表达式
lambda本质上是我们定义了一种叫做匿名函数的方式,就像是一个快速的一次性函数,展示一下需要运行的代码。是一个快速创建函数对象的语法糖。
只要你有一个函数指针,你都可以在c++中使用lambda。这就是它的原理,所以lambda是我们不需要通过函数定义就可以定义一个函数的方式。lambda的用法是,在我们会设置函数指针指向函数的任何地方,我们都可以将它设置为lambda。
以下来自C++11:lambda表达式_lambda表达式c++11-CSDN博客
https://blog.csdn.net/zhang_si_hang/article/details/127117260
lambda表达式书写格式: [capture-list] (parameters) mutable -> return-type { statement
}
[capture-list] (parameters) mutable -> return-type { statement }
捕捉列表 函数参数 去const(单词意思易变的) 返回值 函数体
auto Add1 = [ ](int x, int y)->int {return (x + y); };
lambda表达式各部分说明
(1)[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置, 编译器根据[]来
判断接下来的代码是否为lambda函数, 捕捉列表能够捕捉上下文中的变量供lambda 函数使用。
捕捉的参数都是自带const的,想要去掉const属性可以用mutable,但是捕捉的对象(变量)仍是一份拷贝,外部的a,b无法被修改,所以mutable 很少用,意义不大,不如用 [&]引用捕捉 ( )引用传参
(2)(parameters):参数列表。与 普通函数的参数列表一致,如果不需要参数传递,则可以
连同()一起省略。所传参数和捕捉参数不一样,不自带const,可以修改
(3)mutable: 默认情况下,lambda函数总是一个const函数,mutable可以取消其常量
性。 使用该修饰符时,参数列表不可省略(即使参数为空时,也要带上小括号)。mutable 只是让传值捕捉变量const属性去掉了,但是捕捉的a,b仍是拷贝,外部的a,b无法被修改,所以mutable 很少用,意义不大
(4) ->returntype:返回值类型。用 追踪返回类型形式声明函数的返回值类型,没有返回
值时此部分可省略。 返回值类型明确情况下,也可省略,由编译器对返回类型进行推 导。
(5) {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获
到的变量。
C++的命名空间
命名空间(namespace)
c++为解决重名问题设计了namespace命名空间语法,定义格式为namespace xx {},namespace是关键字,xx是自定义的空间名称,大括号是范围限定,也就是括号内是一个整体空间,可以有任何东西,如变量、函数等,括号内可以直接引用,而括号外的想相互访问必须指定空间名称+内部名称,namespace看起来就像是一种前缀。namespace本质上就是改变全局变量或函数的链接属性,即改变作用域。
对于没有命名空间的语言,比如说c语言的glfw库,他为了避免重名就会让每个函数前面加上GLFW的前缀,比如GLFWInit,在c++中,则可以不这样子做.你使用c++写GLFW库就可以直接用Init。
同时,类其实也是一种命名空间。
然后是直接使用using namespace在代码最前面坏处,比如using namespace std。当你引入了很多库函数的时候,你虽然使用using namespace std就可以把std::cout变成cout了,但是,你不会真的这个cout是标准库里面的还是你魔改stl里面的(一个例子,其他不常使用的命名空间就更能看出来),所以说,滥用using namespace将会导致它不再能很好地解决重名的问题。所以使用std::cout吧,你将一眼看出来这个函数是哪个命名空间的。
最后,对于using namespace,你要尽量将这些限制在一个小的作用域下,如果可以比如说就在一个if内或者是一个function内部写,永远不要在头文件内写using namespace。
一些用法
#include<iostream>
namespace namespace_a
{
namespace namespace_b
{
void foo()
{
std::cout<<"hello"<<std::endl;
}
}
}
int main()
{
namespace hub = namespace_a::namespace_b;//给嵌套的命名空间取别名
hub::foo();
std::cin.get();
}
C++的线程
#include<iostream>
#include<string>
#include<vector>
#include<array>
#include<thread>
static bool s_Finished = false;
void DoWork() {
using namespace std::literals::chrono_literals;
while (!s_Finished) {
std::cout << "working...." << std::endl;
std::this_thread::sleep_for(1s);//顾名思义,std::this_thread指这个线程,this_thread::sleep_for(1s)指让这个线程休眠一秒
}
std::cout << "finished" << std::endl;
}
int main()
{
std::thread worker(DoWork);//这里既创建了线程又启动了这个线程,
//另外一个线程一直执行dowork函数,同时主线程继续执行
std::cin.get();
s_Finished = true;
worker.join();//线程加入,它做的事情是,嘿,你能在当前线程上等待这个线程完成它的工作吗?
//因此阻塞当前线程,直到另外一个线程完成
//因为这个东西是并行运行的,我们的主线程开始一个工作线程,写这个join调用的目的是,
//在主线程上等待工作线程完成所有的执行之后,再继续执行主线程
//所以在这里的效果就是,主线程执行到这里就等着工作线程结束,工作线程结束后主程序再执行下一个语句
//以防止一些清理或者等待工作没有做完程序就结束了导致报错
std::cin.get();
}
C++的计时
C++11后有chrono,它是C++库的一部分,不需要去使用操作系统库。但是在这之前如果想要一个非常精确的计时器,那么需要使用操作系统库,如Windows 中有QueryPerformanceCounter,事实上如果想要更多的控制计时器,控制CPU的计时能力,那么你可能会使用平台特定的库。
Cherno库非常好,可以高精度计时,它几乎适用于所有平台。所以建议使用这个方法来满足你所有的时间(计时)需求,除非你正在做一些特定的底层的事情。
std::endl因为某些原因非常慢,换成\n可以显著提高速度(实际测试里面,只需要运行5ms左右的程序两者没有差距甚至endl还要快一些,但是对于运行300ms的程序,\n比endl要快大概1/3),
#include<iostream>
#include<string>
#include<vector>
#include<array>
#include<thread>
#include<chrono>
int main()
{
using namespace std::literals::chrono_literals;//为了使用1s这种东西
auto start = std::chrono::high_resolution_clock::now();//当前时间
std::this_thread::sleep_for(1s);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> duration = end - start;//duration:持续时间,精度为float
std::cout << duration.count() << std::endl;//duration.count()返回的模板的值,这里是float
std::cin.get();
}
下面是优化版
#include<iostream>
#include<string>
#include<vector>
#include<array>
#include<thread>
#include<chrono>
struct Timer //使用整个对象生存期之类的范例,让它自动为我计时
{
std::chrono::time_point<std::chrono::steady_clock> start, end;//现在知道auto的好处了吧?
std::chrono::duration<float> duration;
Timer() {//构造函数
start = std::chrono::high_resolution_clock::now();//当前时间
}
~Timer() {//析构函数
end = std::chrono::high_resolution_clock::now();
duration = end - start;
std::cout << duration.count()*1000 <<"毫秒"<< std::endl;
}
};
void Function() {
Timer timer;//函数开始调用时创建,调用结束后析构
for (int i = 0; i < 10000; i++) {
std::cout << "hello\n" ;
}
}
int main()
{
using namespace std::literals::chrono_literals;//为了使用1s这种东西
Function();
std::cin.get();
}
C++多维数组
比如二维数组,实际上,它只是数组的数组
#include<iostream>
#include<string>
#include<vector>
#include<array>
#include<thread>
#include<chrono>
int main()
{
int ** array2d = new int*[50];//分配了一个包括了50个元素的指针数组的大小的内存
//new所做的只是分配了内存,其他只是会你做一个类型检查罢了
//而类型只是一种语法,设置类型是用来处理数据的
for (int i = 0; i < 50; i++) {
array2d[i] = new int[50];//array就是一个数组的数组,每一个元素就是一个数组
}
array2d[0][0] = 0;//访问二维数组元素的方法
for (int i = 0; i < 50; i++) {
delete[] array2d[i];//必须要先释放每个数组的空间,然后再释放指针数组的空间,
}
delete[] array2d;
std::cin.get();
}
另外就是,如果你这样子处理数组的数组,会造成内存碎片的问题,因为每个数组都被独立地分配了空间,随机在内存中,他们可能之间隔得很远(堆上面的内存是由空闲链表进行随机分配的),所以遍历的时候可能会导致缓存不命中(cache miss),就会浪费时间从ram中获取数据,所以,他的遍历会比同等大小的一维数组慢很大,事实上,当你想要优化代码提高性能的时候,优化内存访问算很重要的事情,所以啊,其实可以考虑使用一个同等大小的一维数组来做二维数组什么的。
C++的排序
自带函数排序
#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
int main()
{
std::vector<int> values = { 3,5,1,4,2 };
std::sort(values.begin(), values.end(), std::greater<int>());//values.begin(),values.end()指定了排序范围,
//std::greater<int>()表示从大到小排序,没有的时候,它默认对整数升序排序
std::cin.get();
}
自己写lambda进行排序
#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
int main()
{
std::vector<int> values = { 3,5,1,4,2 };
std::sort(values.begin(), values.end(), [](int a, int b) {return a < b ? true : false; });//对于我们自己写的函数
//如果返回true,就意味着a会排在b的前面,反之在后面,所以这样子写就升序排序
for (int value:values){
std::cout << value << std::endl;
}
std::cin.get();
}
#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
int main()
{
std::vector<int> values = { 3,5,1,4,2 };
std::sort(values.begin(), values.end(), [](int a, int b) {
if (a == 1)
return false;
else if(b==1)
return true;
return a < b;
});//这样子写就是升序排序,只是1到最后面去了
for (int value:values){
std::cout << value << std::endl;
}
std::cin.get();
}
C++的类型双关
类型双关(type punning)只是一个花哨的术语,用来在C++中绕过类型系统。
C++是一个强类型语言,就是说我们有一个类型系统,而像JavaScript就没有变量类型的概念。
然而,C++的这种类型系统并不像在其他语言当中那样强制,比如java,他们的类型很难绕开,包括C#也是,你虽然可以绕开类型系统,但要做更多的工作。在C++中虽然类型是由编译器强制执行的,但你可以直接访问内存。
#include<iostream>
int main()
{
int a = 50;
double d = *(double*)(&a);//这样子就可以了,但是注意,这个例子访问了四字节不属于原本自己的内存
std::cout << d << std::endl;
std::cin.get();
}
cpp可以非常自由地操控内存(
#include<iostream>
struct Entuty
{
int x, y;
};
int main()
{
Entuty entity = { 5,8 };
int* x = (int*)&entity;
std::cout << x[0] << " " << x[1]<<std::endl;
std::cin.get();
}
是这样的,cpp是自由的(
C++的联合体
共用内存,所有的成员一起并且同时使用这个内存。你可以像使用结构体或者类一样使用它们,你也可以给它添加静态函数或者普通函数、方法等待。然而你不能使用虚方法,还有其他一些限制。但是通常人们用联合体来做的事情,是和类型双关紧密相关的。
当你想要给一个变量取两个名字的时候,它很有用。
通常union是匿名使用的,但是匿名union不能含有成员函数。
一个例子
int main()
{
struct Union
{
union
{
float a;
int b;
};
};
Union u;
u.b = 4;
u.a = 1.5;
std::cout << u.a<<","<<u.b << std::endl;
std::cin.get();
}
#include<iostream>
struct Vector2
{
float x, y;
};
struct Vector4
{
union
{
struct
{
float x, y, z, w;
};
struct
{
Vector2 a, b;//这样子就可以多出来一种访问内存方式
//可以之间以二维向量的形式访问,也可以只访问单个元素了
//同时,两种访问方式访问都是同一个内存,不必内存转换或者额外存储
};
};
};