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"; }