Cherno CPP学习笔记-05-中级特性

1.16、再摸!

P56、C++的auto关键字

auto让C++ “有点”变成了弱类型语言

  • 强类型语言中必须指定类型。

如果改变了api,客户端调用api使用auto接收可以不用修改代码,但客户端可能会因为类型的改变而出错。

模板中常用auto,因为不得不。

一个比较好的使用场景:(名字很长的类型的迭代)

int main()
{
	std::vector<std::string> strings;
	strings.push_back("Apple");
	strings.push_back("Orange");
	strings.push_back("Banana");

	for (std::vector<std::string>::iterator it = strings.begin();
		it != strings.end(); it++)
	{
		std::cout << *it << std::endl;
	}

	for (auto it = strings.begin();
		it != strings.end(); it++)
	{
		std::cout << *it << std::endl;
	}

	std::cin.get();
}
//也可以用using XXX = std::vector<std::string>::iterator;
//XXX it;
//还可以用typedef

C++14中还可以函数后置返回类型填auto

C++11中可以 函数名->char*

P57、C++的静态数组(std::array)

array相比普通数组的优势

  • array.size();
  • array支持迭代器:.begin()和.end()支持foreach迭代、支持STL算法。
  • 使用迭代器遍历,可以让程序员不关心实际大小。
  • array有边界检查,编译模式可以设置ITERATOR_DEBUG_LEVEL;宏(不同版本名字好像不一样),在调试模式更强大

std::array存储在栈中

//一个不完美的模板
template<int N>
void PrintArray(const std::array<int, N> array)
{
	for (int i = 0; i < N; i++)
	{
		std::cout << array[i] << std::endl;
	}
}

int main()
{

	std::array<int, 3> datas;
	datas[0] = 1;
	datas[1] = 2;
	datas[2] = 3;
	PrintArray<3>(datas);


	std::cin.get();
}

array源码:(边界检查)

    _NODISCARD reference operator[](size_type) noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_REPORT_ERROR("array subscript out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0

        return *data();
    }

array源码:size()返回模板参数,即不实际存储size的大小

template <class _Ty, size_t _Size>

......

_NODISCARD constexpr size_type size() const noexcept {
        return _Size;
    }

1.17、三连摸

P58、C++的函数指针

语法:

#include<iostream>
void HelloWorld()
{
	std::cout << "Hello World!" << std::endl;
}

void Print(int a)
{
	std::cout << a << std::endl;
}

int main()
{
	auto function = HelloWorld;
	function();

	void(*fun)();  //type::  void(*)()  name::fun
	fun = HelloWorld;
	fun();


	typedef void(*HelloWorldFunction)();
	HelloWorldFunction func = HelloWorld;
	func();

	void(*Pri)(int) = Print;
	Pri(8);

	std::cin.get();
}

用途:

#include<iostream>
#include<vector>

void Print(int a)
{
	std::cout << "value:" << a << std::endl;
}

void ForEach(std::vector<int> values, void(*function)(int))
{
	for (int var : values)
		function(var);
}


int main()
{
	std::vector<int> values = { 1,3,2,4,7 };
	ForEach(values,Print);
	//lambda函数引子:
    ForEach(values, [](int a) {std::cout << "values" << a << std::endl; });
	std::cin.get();
}

P59、C++的lambda(C++11)

lambda本质上是我们定义一种叫做匿名函数的方式,我们用这种方式创建函数,不需要实际创建一个函数

就像是一个快速的一次性函数,展示下需要运行的代码,比起正式的函数,我们更想将它视为一个变量

lambda的用法是,在我们会设置函数指针指向函数的任何地方,我们都可以将它设置为lambda

参考文档:Lambda expressions (since C++11) - cppreference.com

第一个的方括号就是说我们打算如何传递变量

[=]通过值传递传递所有的变量;

[&]通过引用传递传递所有的变量;

[&a,b]传a的引用,b的值

#include<functional>

void ForEach(std::vector<int> values, const std::function<void(int)>& func)
{
	for (int var : values)
		func(var);
}


int main()
{
	std::vector<int> values = { 1,3,2,4,7 };
	
	int a = 5;

	ForEach(values, [&a](int value) { std::cout << "values" << value << a << std::endl; });

	std::cin.get();
}
#include<algorithm>
	std::vector<int> values = { 1,3,2,4,7 };
	auto it = std::find_if(values.begin(), values.end(), [](int value) { return value > 3; });
	std::cout << *it << std::endl; //4

P60、为什么不使用using namespace std

方便阅读时辨认来源,std::代表是std的东西;

使用的话追踪错误很难。

总结:没什么东西。

P61、C++的名称空间(namespace)

避免命名冲突。

避免产生两个相同的符号,产生链接错误。

类也是一个namespace

P62、C++的线程

#include<iostream>
#include<thread>

static bool s_Finished = false;

void DoWork()
{
	using namespace std::literals::chrono_literals;

	std::cout << "thread id: " << std::this_thread::get_id() << std::endl;

	while (!s_Finished)
	{
		std::cout << "Working..." << std::endl;
		std::this_thread::sleep_for(1s);
	}
}

int main()
{
	std::thread worker(DoWork);

	std::cin.get();
	s_Finished = true;

	worker.join();

	std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
	std::cin.get();
}

1.18、不想学习

P63、C++的计时(时间)

高精度计时 std::chrono

#include<iostream>
#include<thread>
#include<chrono>


int main()
{
	using namespace std::literals::chrono_literals;

	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;
	std::cout << duration.count() << "s cost" << std::endl;


	std::cin.get();
}

利用std::chrono创建计时器,计时函数

#include<iostream>
#include<thread>
#include<chrono>

struct Timer
{
	std::chrono::time_point<std::chrono::steady_clock>start, end;
	std::chrono::duration<float> duration;

	Timer() 
	{
		start = std::chrono::high_resolution_clock::now();
	}
	~Timer()
	{
		end = std::chrono::high_resolution_clock::now();
		duration = end - start;
		float ms = duration.count() * 1000.0f;
		std::cout << "Timer took " << ms << "ms" << std::endl;
	}
};

void Function()
{
	Timer timer;

	for (int i = 0; i < 10000; i++)
		std::cout << "Hello" << std::endl;//Timer took 3741.08ms
		//std::cout << "Hello\n";//Timer took 3503.22ms
}

int main()
{
	Function();

	std::cin.get();
}

P64、C++多维数组

分配原理:

int main()
{
	int** a2d = new int*[50];
	for (int i = 0; i < 50; i++)
		a2d[i] = new int[60];
	for (int i = 0; i < 50; i++)
		delete a2d[i];
	delete a2d;


	int*** a3d = new int** [50];
	for (int i = 0; i < 50; i++)
	{
		a3d[i] = new int* [60];
		for (int j = 0; j < 60; j++)
		{
			a3d[i][j] = new int[70];
		}
	}
	
    //delete部分略
	std::cin.get();
}

上面这个例子会造成内存碎片问题,内存跳跃也会导致遍历速度慢(cache miss),可以降维优化内存访问。

P65、C++的排序

#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>


int main()
{
	std::vector<int> values = { 3,5,4,1,2 };
	//升序  <
	std::sort(values.begin(), values.end());
	//降序  >
	std::sort(values.begin(), values.end(), std::greater<int>());
	std::sort(values.begin(), values.end(), std::greater_equal<int>());
	std::sort(values.begin(), values.end(), [](int a, int b) {
		//让 1 移到最后  23451
		if (a == 1)return false;
		else if (b == 1)return true;
		return a < b; 
	});

	std::cin.get();
}

P66、C++的类型双关

“把拥有的内存段当成不同类型的内存来对待”,自由地操作内存。

#include<iostream>

int main()
{
	int a = 50;
	double value = *(double*)&a;
	std::cout << value << std::endl;	//-9.25596e+61
	//value 和 a 的起始地址相同
	std::cin.get();
}

1.24、摸了

P67、C++的联合体

union只有一个真正的内存成员,不同成员占用相同的内存空间。

C++中的联合体通常被用来做类型双关。可以轻易的做到**“把拥有的内存段当成不同类型的内存来对待”**

struct Union {
    union {
        float a;
        int b;
    };
};
Union u;
u.a = 2.0f;
std::cout << u.a << "," << u.b << std::endl;
struct Vector2 
{
	float x, y;
};
struct Vector4
{
	//匿名
	union 
	{
		struct 
		{
			float x, y, z, w;
		};
		struct 
		{
			Vector2 a, b;
		};
		
	};
};
void PrintVector2(const Vector2& vector)
{
	std::cout << vector.x << ", " << vector.y << std::endl;
}
int main()
{
	Vector4 vector = { 1.0f,2.0f,3.0f,4.0f };//union匿名所以可以直接这样写
	PrintVector2(vector.a);
	PrintVector2(vector.b);
	vector.z = 500.0f;
	PrintVector2(vector.a);
	PrintVector2(vector.b);//500, 4

	std::cin.get();
}

P68、C++的虚析构函数

容易出错:多态情况下子类的析构函数可能会不被调用

#include<iostream>
class Base
{
public:
	Base() { std::cout << "Base Constructor\n"; }
	~Base() { std::cout << "Base Destructor\n"; }
};
class Derive : public Base
{
public:
	Derive() { std::cout << "Derive Constructor\n"; }
	~Derive() { std::cout << "Derive Destructor\n"; }
};
int main()
{
	Base* base = new Base();
	delete base;
	//Base Constructor 
	//Base Destructor
	
	Derive* derive = new Derive();
	delete derive;
	//Base Constructor
	//Derive Constructor
	//Derive Destructor
	//Base Destructor


	//多态
	Base* poly = new Derive();
	delete poly;
	//Base Constructor
	//Derive Constructor
	//Base Destructor
	//Base* poly没有调用子类的析构函数,会导致内存泄露。
	std::cin.get();
}

解决方案:将基类析构函数virtual修饰,意为这个类有可能被扩展为子类,可能还有一个析构函数需要调用。

virtual ~Base() { std::cout << "Base Destructor\n"; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Nicer0815

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

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

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

打赏作者

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

抵扣说明:

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

余额充值