cpp学习日记8(只是记录给未来的自己看的)

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;//这样子就可以多出来一种访问内存方式
			//可以之间以二维向量的形式访问,也可以只访问单个元素了
			//同时,两种访问方式访问都是同一个内存,不必内存转换或者额外存储
		};
	};
};
  • 34
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值