C++11相关知识点

auto、decltype自动类型推导

auto并不代表实际的数据类型,且auto并不是在任何场合下都能推导出变量的实际类型,使用auto必须初始化。

auto 变量名 = 变量值;

当变量不是指针或引用类型时,推导的结果中不会保留const、volatile关键字;

当变量是指针或引用类型时,推导的结果中会保留const、volatile关键字。

不允许使用auto四个场景:

1.不能作为函数参数使用

int func(auto a,auto b)//error
{cout << a << "" << endl;
}

2. 不能用于类的非静态成员初始化

class Test
{auto a1 = 0;//errorstatic auto a2 = 0;//error 类的静态非常量成员不允许在类内直接初始化static int a;static const auto a3 = 0;//ok 
};
auto Test::a = 1;//error

3.不能使用auto关键字定义数组

int func()
{int array[] = { 1,2,3,4,5 };auto t1 = array;//ok t1被推导为int*类型auto t2[] = array;//error auto无法定义数组auto t3[] = { 1,2,3,4,5 };//error auto无法定义数组
}

4.无法用auto推出模板参数

template<typename T>
struct Test {T a};
int func()
{Test<double>t;Test<auto>t1 = t;//error 无法推出模板类型
}

相比于auto用于推导变量类型,decltype则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算。

decltype(表达式)

	const int& i = 1;decltype(i) b = 1;//b是const int&int a = 2;decltype(a + 3.14) c = 5.15;//c是double

增强for

for(declaration:expression)

{

        //循环体
}

declaration表示遍历声明,在遍历过程中,会将遍历到的元素拷贝到声明的变量中,expression是要遍历的对象,它可以是表达式、容器、数组、初始化列表等。

lambda表达式

概念

sort(vec.begin(),vec.end,[ ](int a,int b){return a>b;}); //作为匿名函数使用

表示形式

具体语法

解释:

举例:

能不能改变:int a=100;

  1. [ = ](){return a = 10; } //错误 常函数,a是值传递,不可以改变
  2. [ & ](){return a = 10;} //可以 a是引用传递,a会被改写成10
  3. [ = ]()mutable{return a = 10;} //可以 mutable 可变,副本a会被改写成10,外面a不变
  4. [ & ]()mutable{return a = 10:} //可以 a是引用传递,mutable 可变,a会被改写成10

#include<iostream>
using namespace std;class AA
{
public:AA(){auto f0 = [this](){return this;};cout<<f0()<<endl;}
};int val1 = 10;
int main()
{auto f1 = [](){return val1 = 100;}; //全局的变量,不用捕获,可以直接使用cout<<f1()<<endl; //100cout<<val1<<endl; //100int j = 5;auto f2 = [&j](){return j = 10;};auto f3 = [j](){return j;}; //先后执行会有不同cout<<f2()<<endl; //10cout<<j<<endl; //10//修改的是副本auto f4 = [j]()mutable{return j = 100;};auto f5 = [j]()mutable{return j = 200;};cout<<f4()<<endl; //100cout<<j<<endl; //10cout<<f5()<<endl; //200cout<<j<<endl; //10auto f6 = [&j]()mutable{return j = 300;};cout<<f6()<<endl; //300cout<<j<<endl; //300AA a;return 0;
}

智能指针

头文件:#include<memory>

auto_ptr(已在C++11中弃用)

创建方式:

  1. 有参构造
  2. reset()方法
  3. 赋值

存在的问题:指针的所有权交出的太随意,交出后,原指针为空,后续通过该指针调用会出问题。

unique_ptr

唯一所有权智能指针,在auto_ptr的基础上禁止拷贝构造与赋值,通过move()对使用权进行转移,避免auto_ptr在用户无意识情况下将指针的所有权交出。

使用场景:单例模式

shared_ptr

#include <iostream>
#include<vector>
#include<string>
#include<memory>
using namespace std;class AAAA{
private:string s;
public:AAAA( const string str){s = str;cout<<"AAAA:"<< s << endl;}~AAAA(){cout<<"~AAAA" << endl;}void say(){cout<<"say:"<< s << endl;}
};int main() {shared_ptr<AAAA> sp1( new AAAA("shared_ptr1"));shared_ptr<AAAA> sp2;sp2.reset( new AAAA("shared_ptr2"));shared_ptr<AAAA> sp3;sp3 = make_shared <AAAA>("share_ptr3");{shared_ptr<AAAA>sp4;sp4=sp3; //指向同一个 共享cout<< sp4.use_count()<< endl;//2}sp3.get()->say();//say:share_ptr3sp3->say();//say:share_ptr3cout<<sp3.use_count()<<endl;//查看引用计数 1sp3.reset();//主动回收空间return 0;
}

存在的问题: 

1、循环引用

什么是循环引用?举例:

解释:此时spc.use_count()为2,spb.use_count()也为2,走到{ }的结尾处,对pc,pb回收,此时引用计数都变为1,空间无法回收,这就是循环引用。

2、多线程下的线程安全问题

为了保证空间正确回收,引用计数+1或-1是线程安全的,但关于指针指向空间的使用,是非线程安全,所以在多线程下使用这个空间要加锁。

weak_ptr

同时在weak_ptr销毁时,对应空间的引用计数也不会减1。

weak_ptr,弱引用计数智能指针,它的提出主要是解决shared_ptr中循环引用的问题。

怎么解决?举例:

解释:将spb或spc由shared_ptr改成weak_ptr,这里我们改spb。此时spc.use_count()为1,spb.use_count()为2,回收pc、pb后,spc.use_count()为0,回收其空间,当该空间回收后,该空间的spc对象也就不存在了,这时spb.use_count()也由1变为0,对应的空间也得到了回收。

  1. 可以使用 lock()方法获取与weak_ptr捆绑的shared_ptr。lock() 方法会尝试升级 weak_ptr 到一个 shared_ptr。如果 weak_ptr 所指向的对象仍然存在(即没有被销毁),lock() 方法会成功并返回一个 shared_ptr;如果对象已经被销毁,它会返回一个空的 shared_ptr。
  2. weak_ptr不能直接调用函数,如果想调用,需要先获得与weak_ptr捆绑的shared_ptr,再通过shared_ptr进行调用。
  3. expire()方法用于检查 weak_ptr 是否指向一个有效的 shared_ptr。如果 weak_ptr 所指向的对象已经被销毁(即所有与之关联的 shared_ptr 都已被释放),expire() 将返回 true,表示 weak_ptr 失效。

举例:

#include <iostream>
#include <memory>class MyClass {
public:void doSomething() {std::cout << "Doing something" << std::endl;}
};int main() {std::shared_ptr<MyClass> sp(new MyClass());std::weak_ptr<MyClass> wp(sp); // 从 shared_ptr 创建 weak_ptr//std::weak_ptr<MyClass> wp = sp;// 模拟 shared_ptr 被销毁的情况sp.reset();if(wp.expired()){std::cout<<"weak_ptr失效"<<std::endl;//weak_ptr失效}// 尝试通过 weak_ptr 获取 shared_ptrstd::shared_ptr<MyClass> lockedSp = wp.lock();if (lockedSp) {lockedSp->doSomething(); // 如果 shared_ptr 不为空,调用成员函数} else {std::cout << "Object has been destroyed." << std::endl;//Object has been destroyed.}return 0;
}

总结:

右值引用

概念

引用分为左值引用,右值引用,常引用。

返回值优化

  • 当函数需要返回一个对象实例的时候,就会创建一个临时对象并通过拷贝构造函数将目标对象复制到临时对象,这里有拷贝构造函数和析构函数会被多余的调用到,有代价。而通过返回值优化,C++标准允许省略调用这些拷贝构造函数。

举例:

左值引用与右值引用举例:

int main()
{//以下的都是左值int* p = new int(0);int a = 1;const int b = 2;//以下是对上面左值的左值引用int*& rp = p;int& ra = a;const int& rb = b;int& pvalue = *p;return 0;
}
int main()
{double x = 1.1, y = 2.2;//以下是常见的右值10;x + y;fmin(x, y);//以下是对右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);//这里编译会报错:“error C2106: “=”: 左操作数必须为左值”10 = 1;x + y = 1;fmin(x, y) = 1;return 0;
}

表示形式

举例:

总结:左值引用不可引用右值,右值引用也不可引用左值,但可以引用移动的左值。

作用

  1. 引用临时对象,延长临时对象生命周期
  2. 实现移动语义完美转发,消除两个对象交互时不必要的对象拷贝
  3. 能够更简洁明确地定义泛型函数。(更准确的传参和调用函数)

解释:

转移语义可以将资源(堆,系统对象等)从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高C++应用程序的性能

举例:

#include<iostream>
using namespace std;#include<vector>vector<int> getVec(vector<int>& vec)
{cout<<"fun::vec addr:"<<&vec.front()<<endl; //fun::vec addr:0x1e1275d0return vec;
}vector<int>&& getVec2(vector<int>& vec)
{cout<<"fun::vec2 addr:"<<&vec.front()<<endl; //fun::vec2 addr:0x1e1275d0return move(vec);
}int main() {//转移语义vector<int> vec = {1,2,3,4,5,6};cout<<"vec addr:"<<&vec.front()<<endl; //vec addr:0x1e1275d0vector<int> vec1 = getVec(vec);cout<<"vec1 addr:"<<&vec1.front()<<endl; //vec1 addr:0x1e1285f8vector<int> vec2 = getVec2(vec);cout<<"vec2 addr:"<<&vec2.front()<<endl; //vec2 addr:0x1e1275d0cout<<"vec items:"<<endl;for(auto& num : vec){cout<<num<<" "; //空}cout<<endl;for(auto& num : vec2){cout<<num<<" "; //1 2 3 4 5 6 }cout<<endl;return 0;
}

右值引用可以实现精确传递

  • 精确传递适用场景:需要将一组参数原封不动的传递给另一个函数。
  • 精确传递就是在参数传递过程中,所有属性和参数值都不能改变。
  • 在泛型函数中,这样的需求非常普遍。 

举例:

#include<iostream>
using namespace std;template <typename T>
void forward_value(const T& val)
{cout<<"forward_value(const T& val)"<<endl;
}template <typename T>
void forward_value(T& val)
{cout<<"forward_value(T& val)"<<endl;
}int main() {//精准传参int a = 0;const int &b = 1;forward_value(a); //int&forward_value(b); //const int&forward_value(2); //const int&return 0;
}

由上述代码可知,常引用以及常量匹配的都是const int&,那么我们再加上右值引用,会发生什么呢?

#include<iostream>
using namespace std;template <typename T>
void forward_value(const T& val)
{cout<<"forward_value(const T& val)"<<endl;
}template <typename T>
void forward_value(T& val)
{cout<<"forward_value(T& val)"<<endl;
}template <typename T>
void forward_value(T&& val)
{cout<<"forward_value(T&& val)"<<endl;
}int main() {//精准传参int a = 0;const int &b = 1;forward_value(a); //int&forward_value(b); //const int&forward_value(2); //int&&return 0;
}

加上右值引用后,结果发生了变化,可见右值引用可以实现精准传参。

问题: 

四种类型转换

C语言里面有强制类型转换,为什么不继续沿用,而存在这四种类型转换?

  • C++在设计时,强调类型安全,但是C中的类型转换可谓无所不能,沿用的话,会破坏C++的类型系统,还会留下很多安全隐患。
  • 此外,四种类型转换还有他们使用的不同场合和功能,组合使用较C中的强制类型转换更强大。

static_cast 静态转换

编译时确定的

使用场景:

  • 相关内容的转换 例:数与数之间、int short char 之间、int*与 void*之间
  • 继承中对象的向上和向下转换(向下转换,没有检查,可能会有问题)

举例:

父类A 子类 A1(fun1) A2(fun2) A3(fun3)
A* pa= new A1;
//向下转换
A2 *pa2 = static_cast<A2*>(pa); // 转换可以,但是后面使用就出问题了
pa2 -> fun2();

注:

  • A是B的父类,B->A 继承中对象的向上转换,A->B 向下转换
  • 不相关的不能转换:student*与int*之间不能转换,char*与int*之间也不能(因为不安全),int -> int* 不可以,int*->int 也不可以

相当于限制版的强制类型转换

代码举例:

#include<iostream>
using namespace std;class student{};
class A1
{
public:virtual void fun(){}
};
class A2:public A1{};
class A3:public A1{};
class A4:public A2{};int main() {int a = 0;int *p = &a;char c = 0;char* pc = &c;student st;{//---------------static_cast-----------------//相关内容转换//p = static_cast<int*>(pc);//char*->int* 不允许void* lpvoid = static_cast<void*>(pc);a = static_cast<int>(c);//student* ps = static_cast<student*>(p);//int*->student* 不允许//p = static_cast<int*>(&st);//student*->int* 不允许//a = static_cast<int>(p);//int*->int 不允许//p = static_cast<int*>(a);//int->int* 不允许A2* pa2 = (A2*)(new A1);//子类指针默认无法指向父类对象,除非强转//继承关系中对象的向上转换A1* pa1 = static_cast<A1*>(pa2);pa1 = new A2;//继承关系中对象的向下转换pa2 = static_cast<A2*>(pa1);A3* pa3 = static_cast<A3*>(pa1);if(pa3){cout<<"pa3 is valid"<<endl;//pa3会取到值,但后续如果执行A3里面的函数会有问题}//C语言强制类型转换student* ps2 = (student*)(p);p = (int*)&st;}return 0;
}

dynamic_cast 动态转换

  1. 只适用于继承上的两个类之间转换
  2. 继承的向上转换效果等同于static_cast
  3. 继承的向下转换会进行检验
  4. 向下转换安全(即父类指针要转换为子类指针,恰巧这个父类指针指向这个子类对象,可以得到对应结果)
  5. 向下转换不安全(即父类指针要转换为子类指针,恰巧这个父类指针不指向这个子类对象,返回结果为空)
  6. 向下转换,父类中必须包含虚函数(static_cast 没有这个要求)
  7. 特殊情况
  • 向下转换安全(即父类指针要转换为子类指针,恰巧这个父类指针指向这个子类的子类),通俗来说就是爷爷指向孙子,此时爷爷想转化为父亲。

代码举例:

A1、A2、A3、A4之间关系图:

#include<iostream>
using namespace std;class student{};
class A1
{
public:virtual void fun(){}
};
class A2:public A1{};
class A3:public A1{};
class A4:public A2{};int main() {int a = 0;int *p = &a;char c = 0;char* pc = &c;student st;{A2* pa2 = (A2*)(new A1);//继承关系中对象的向上转换A1* pa1 = dynamic_cast<A1*>(pa2);pa1 = new A2;//继承关系中对象的向下转换pa2 = dynamic_cast<A2*>(pa1);A3* pa3 = dynamic_cast<A3*>(pa1);if(pa3){cout<<"pa3 is valid"<<endl;}else{cout<<"pa3 is invalid"<<endl;//pa3 is invalid}//特殊情况,向下转换安全:父类指针要转换为子类指针,恰巧这个父类指针指向这个子类的子类pa1 = new A4;pa2 = dynamic_cast<A2*>(pa1);if(pa2){cout<<"pa2 is valid"<<endl;//pa2 is valid}else{cout<<"pa2 is invalid"<<endl;}pa1 = new A2;A4* pa4 = dynamic_cast<A4*>(pa1);if(pa4){cout<<"pa4 is valid"<<endl;}else{cout<<"pa4 is invalid"<<endl;//pa4 is invalid}//交叉转换//pa3 = static_cast<A3*>(pa2); //相当于A1转A2,A2又转A3,静态转换不可以pa3 = dynamic_cast<A3*>(pa2); //动态转换可以}return 0;
}

为什么要用dynamic_cast?

对于动态转换中继承的向下转换,怎么进行安全检验的? 

根据虚函数的入口地址进行检验,如果虚函数的入口地址相同,那么是安全的。

static_cast与dynamic_cast区别?

  1. static_cast 支持相关数据类型转化,dynamic_cast不支持
  2. 对于继承关系中的转换,static_cast可以向上和向下转换,dynamic_cast向上转换与static_cast一致,向下转换,父类要有虚函数,并且存在检验机制
  3. static_cast不支持交叉转换,只能是向上下,dynamic_cast可以交叉转换

const_cast 去常转换

int* p=const_cast<int*>( const_int_lp);
const->非const
volatile->非volatile

例: const A*-> A* 或者 const A ->A
作用:

  • 由于常对象只能调用常函数,并且对象的属性不能修改,去常转换后,可以调用任何类成员函数,并且可以修改属性值

volatile

  • 防止修饰变量编译优化
  • 避免修饰变量缓存和内存不一致(每次到其地址读取,先写后读)

reinterpret_cast 重解释类型转换

  • 是C++里的强制类型转换符
  • 相当于(type),无所不能
  • 有安全隐患,一般编译实在不过的时候使用

委托构造函数&继承构造函数

什么是委托构造函数?

允许在构造函数中调用同一个类中另外一个构造函数 ,实现代码的简化,但要注意不可出现闭环,下面的例子是链式调用,这是可以的,如果3调用1的同时,1也调用3,这是不可以的。同时构造函数的调用应写在初始化列表中,不应写在函数体内部,否则可能会提示形参重定义。

class Test
{
private:int m_max;int m_min;int m_mid;
public:Test() {};Test(int max){this->m_max = max > 0 ? max : 100;}Test(int max, int min):Test(max)//调用Test(max){this->m_min = min > 0 && min < max ? min : 1;}Test(int max, int min, int mid):Test(max, min)//调用Test(max, min){this->m_mid = mid<max && mid>min ? min : 50;}
};

什么是继承构造函数? 

允许派生类继承基类的构造函数,从而减少代码的重复。在没有继承构造函数之前,派生类需要显示地定义构造函数,并在构造函数中调用基类的构造函数来初始化基类的部分,有了继承构造函数以后,就可以让派生类自动继承并使用基类的构造函数,这样就避免了显示地调用。

class Base {
public:Base(int i) :m_i(i) {}Base(int i, double j) :m_i(i),m_j(j) {}Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}int m_i;double m_j;string m_k;
};
class Child :public Base
{
public:Child(int i) :Base(i) {}Child(int i, double j) :Base(i, j) {}Child(int i, double j, string k) :Base(i, j, k) {}
};int main() {Child c(1, 1.2, "Bob");cout << c.m_i << " " << c.m_j << " " << c.m_k << endl;//1 1.2 Bobreturn 0;
}

引入后:

class Base {
public:Base(int i) :m_i(i) {}Base(int i, double j) :m_i(i),m_j(j) {}Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}int m_i;double m_j;string m_k;
};
class Child :public Base
{
public:using Base::Base;
};int main() {Child c(1, 1.2, "Bob");cout << c.m_i << " " << c.m_j << " " << c.m_k << endl;//1 1.2 Bobreturn 0;
}

constexpr

用于修饰函数和变量,在编译期进行运算,从而提高程序的性能。适用于常量表达式,要求值或函数能够在编译时确定。

#include <iostream>// constexpr 函数
constexpr int factorial(int n) {return n <= 1 ? 1 : n * factorial(n - 1);
}int main() {constexpr int fact5 = factorial(5);  // 编译期求值std::cout << "Factorial of 5 is: " << fact5 << std::endl;// 运行时求值int n = 5;int fact = factorial(n);  // 在运行时计算std::cout << "Factorial of 5 is: " << fact << std::endl;return 0;
}

std::function与std::bind

std::function介绍

std::function是一个函数封装器,它可以存储、调用和传递任何可调用对象。

满足以下条件之一就可称为可调用对象: 

  • 函数指针
  • 类成员(函数)指针
  • lambda表达式
  • bind表达式或其它函数对象
  • 具有operator()成员函数的类对象(传说中的仿函数)
  • 可被转换为函数指针的类对象

std::bind介绍

std::bind可以将可调用对象和参数一起绑定,绑定后的结果使用std::function进行保存,并延迟调用到任何我们需要的时候。通过std::bind,可以在调用函数时提前绑定部分参数,从而简化函数调用的过程,这种方式也被称为“柯里化”(currying),有助于提高代码的可读性和灵活性

std::bind通常有两大作用

  • 将可调用对象与参数一起绑定为另一个std:.function供调用
  • 提前绑定部分参数,简化函数调用

function与bind使用代码

#include<iostream>
using namespace std;int add(int a,int b)
{return a + b;
}
//&add 可调用对象
//function 函数封装器,存储可调用对象
//bind 将可调用对象和部分参数绑定在一起
#include<functional>int main()
{std::function<int(int,int)>f = &add;cout<<"res:"<<f(3,4)<<endl;// res:7//在调用之前 将参数与函数指针捆绑 然后使用function存储//因为已经给定参数 所以类型参数列表int()中为空//因为捆绑的时候已经传完参数 所以调用时不需要传参std::function<int()>f1 = std::bind(&add,3,4);cout<<"res:"<<f1()<<endl;// res:7//当捆绑时不知道需要传递的参数时,可以使用std::placeholders::_1 进行参数占位std::function<int(int ,int)>f2 = std::bind(&add,std::placeholders::_1,std::placeholders::_2);cout<<"res:"<<f2(3,4)<<endl;// res:7return 0;
}

捆绑普通函数

#include<iostream>
using namespace std;int add(int a,int b)
{return a + b;
}#include<functional>//加减乘除都可以实现计算
int getResult(std::function<int(int,int)>f)
{int a = 0,b = 0;cin >> a >> b;return f(a,b);
}
//getResult() 是回调函数,先将函数指针传入(可能包含参数),在需要的时候执行int main()
{std::function<int(int ,int)>f = std::bind(&add,std::placeholders::_1,std::placeholders::_2);int res = getResult(f);cout<<"res:"<<res<<endl;return 0;
}

捆绑类成员函数

#include<iostream>
using namespace std;class AA
{
public:int multi(int a,int b){return a * b;}
};#include<functional>//加减乘除都可以实现计算
int getResult(std::function<int(int,int)>f)
{int a = 0,b = 0;cin >> a >> b;return f(a,b);
}int main()
{AA aa;//将类成员函数指针与对象的地址捆绑(类中非静态成员函数第一个参数默认为this指针)//然后multi还有两个参数 int intstd::function<int(int ,int)>f = std::bind(&AA::multi,&aa,std::placeholders::_1,std::placeholders::_2);int res = getResult(f);cout<<"res:"<<res<<endl;return 0;
}

捆绑静态成员函数

#include<iostream>
using namespace std;class AA
{
public:int multi(int a,int b){return a * b;}static int div(int a,int b){return a / b;}
};#include<functional>//加减乘除都可以实现计算
int getResult(std::function<int(int,int)>f)
{int a = 0,b = 0;cin >> a >> b;return f(a,b);
}int main()
{std::function<int(int ,int)>f =std::bind(&AA::div,std::placeholders::_1,std::placeholders::_2);int res = getResult(f);cout<<"res:"<<res<<endl;return 0;
}

使用function与bind有什么好处?

可以在使用类成员函数时不依赖对象,提前将对象地址绑定到函数指针上

chrono时间库

C++11有了chrono时间库,可以在不同系统中很容易的实现定时功能。

要使用chrono库,需要#include<chrono>,其所有实现均在std::chrono

chrono是一个模版库,使用简单,功能强大,只需要理解三个概念:duration、time_point、clock

#include<iostream>
#include<chrono>
#include<thread>
using namespace std;int main()
{//时间间隔举例std::this_thread::sleep_for(std::chrono::milliseconds(2000));//std::this_thread::sleep_for(std::chrono::seconds(2));cout<<"sleep finish"<<endl;//this_thread代表当前线程 sleep_for休眠 参数传的是chrono的时间间隔//milliseconds 毫秒 seconds 秒/*chrono::millisecondsmilliseconds 类型别名typedef duration<int64_t, milli> milliseconds;typedef ratio<1,1000> milli;ratio<1,1000> 分数 1/1000 因为要使用ms 所以是1s的1/1000chrono::duration 结构体 //模版参数 第一个是数值 第二个是单位std::chrono::milliseconds(2000)std::chrono::duration<int64_t, milli> 2000是int64_t类型的数值*///时间点 打印当前时间std::chrono::system_clock::time_point tp =std::chrono::system_clock::now(); // 获取当前时间点time_t t_t = std::chrono::system_clock::to_time_t(tp);tm* t = std::localtime(&t_t);//    string ti = ctime(&t_t);
//    cout << ti << endl;//获取毫秒auto du = tp.time_since_epoch();//获取Unix时间戳(时间间隔) 单位是秒//duration 时间间隔转换 duration_cast<转换的目标类型>(原值)int ms = chrono::duration_cast<chrono::microseconds>(du).count();ms = ms % 1000;//Unix时间戳 1970 01 01 00:00:00 起始时间(纪元时间)//1970 01 01 00:00:00的格林威治时间 是纪元时间 epoch//例:格林威治时间 00:00:00 对应北京时间 08:00:00  那么此时北京时间为 1970 01 01 08:00:00//当前时间距离纪元时间有多少秒表示时间戳/*时间戳的应用:例如,网站向服务器发送请求,某些服务器为避免重复指令一直发送,服务器通常会进行过滤,即服务器在短时间内如果收到多次重复命令,就进行过滤。但某些情况下,我们就想要获取服务器响应,那我们就加时间戳,让服务器直到我们想获得当前时效的内容,这时候服务器就进行处理。*///打印当前时间printf("%d-%02d-%02d %02d:%02d:%02d %3d\n",t->tm_year +1900/*年份从1900开始*/,t->tm_mon +1/*月份从0开始*/,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec,ms);//计算一段时间间隔 使用稳定时钟std::chrono::steady_clock::time_point begin =chrono::steady_clock::now();std::this_thread::sleep_for(chrono::seconds(1));std::chrono::steady_clock::time_point end =chrono::steady_clock::now();//两个时间点的差 即为时间间隔auto d = end - begin;//转换为以ms为单位int diff = chrono::duration_cast<chrono::milliseconds>(d).count();cout<<"diff:"<<diff<<endl;return 0;}

时间间隔 duration

        chrono::milliseconds
        milliseconds 类型别名
        typedef duration<int64_t, milli> milliseconds;
     
        typedef ratio<1,1000> milli;
        ratio<1,1000> 分数 1/1000 因为要使用ms 所以是1s的1/1000
    
        chrono::duration 结构体 //模版参数 第一个是数值 第二个是单位
        std::chrono::milliseconds(2000)
        std::chrono::duration<int64_t, milli> 2000是int64_t类型的数值

  • 由于各种时间段(duration)表示不同,chrono库提供了duration_cast类型转换函数,用于将duration转换成另一个类型的duration。(例:s转换成ms)
  • duration还有一个成员函数count(),用来表示这一段时间的长度

时间点 time_point

获取当前时间点

std::chrono::system_clock::time_point tp = std::chrono::system_clock::now(); 

注:一个time_point必须有一个clock计时

时钟

system_clock

  • 系统时钟,用于获取系统时间的时钟,由于系统时间可以改变,这个时钟也会随之改变
  • 不是连续的,一般不用系统时钟计算时间间隔

steady_clock

  • 稳定时钟,不受系统时间的影响,是单调递增的,一般用于计算时间间隔

high_resolution_clock

  • 高分辨率时钟,system_clock 或 steady_clock 的别名,到底是哪个受系统影响,提供高分辨率的时间计算

三种时钟总结:

  • 这三种时钟类都提供了一个静态成员函数now()用于获取当前时间,该函数的返回值是一个time_point类型。
  • system_clock除了now()函数外,还提供了to_time_t()静态成员函数。用于将系统时间转换成熟悉的std::time_t类型,得到了time_t类型的值,再使用ctime()函数将时间转换成字符串格式,就可以很方便地打印当前时间了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/889219.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【面试】分布式锁专题

1.你说一下什么是分布式锁 分布式锁是一种在分布式系统环境下实现的锁机制&#xff0c;它主要用于解决&#xff0c;多个分布式节点之间对共享资源的互斥访问问题&#xff0c;确保在分布式系统中&#xff0c;即使存在有多个不同节点上的进程或线程&#xff0c;同一时刻也只有一…

Error relaunching VirtualBox VM process: 5 启动虚拟机时发生了错误

出现错误 一大早起来发现虚拟机打不开&#xff0c;看了虚拟机日志是正常的&#xff0c;还回了个档都不行。 最后我突然想起之前在哪看到过&#xff1a;“完美游戏平台会导致虚拟机的问题。” 解决方法 于是我把完美游戏卸载了&#xff0c;发现&#xff0c;真的&#xf…

MCU、ARM体系结构,单片机基础,单片机操作

计算机基础 计算机的组成 输入设备、输出设备、存储器、运算器、控制器 输入设备&#xff1a;将其他信号转换为计算机可以识别的信号&#xff08;电信号&#xff09;。输出设备&#xff1a;将电信号&#xff08;&#xff10;、&#xff11;&#xff09;转为人或其他设备能理解的…

从零开始:如何在.NET Core Web API中完美配置Swagger文档

目录 新建项目 RestFul Swagger配置 注释展示 版本控制 Token传值 方法封装 新建项目 打开visual studio创建新项目&#xff0c;这里我们选择.net core web api模板&#xff0c;然后输入项目名称及其解决方案创建新项目 这里使用配置一些其他信息&#xff0c;根据自己情…

百度搜索应适用中文域名国家标准,修复中文网址展示BUG

12月1日中文域名国家标准正式实施。该标准“明确了中文域名在编码、解析、注册、字表等方面的技术要求&#xff0c;适用于中文域名注册管理机构、注册服务机构、网络软硬件服务商及终端用户”。 00:23 显然&#xff0c;百度作为网络软硬件服务商&#xff0c;是包括在国家标准的…

Windows安装elasticsearch、Kibana以及IK分词器

一、下载 1.下载elasticsearch 访问官网Download Elasticsearch | Elastic&#xff0c;下载elasticsearch 2.下载 Kibana 访问Download Kibana Free | Get Started Now | Elastic &#xff0c;下载 Kibana 3. IK分词器下载 访问Gitee 极速下载/elasticsearch-analysis-ik选…

第一个C++程序--(蓝桥杯备考版)

第一个C程序 基础程序 #include <iostream>//头⽂件 using namespace std;//使⽤std的名字空间 int main()//main函数 {cout << "hello world!" << endl; //输出&#xff1a;在屏幕打印"hello world!" return 0;}main函数 main 函数是…

Elasticsearch Serverless 中的数据流自动分片

作者&#xff1a;来自 Elastic Andrei Dan 在 Elastic Cloud Serverless 中&#xff0c;我们根据索引负载自动为数据流配置最佳分片数量&#xff0c;从而使用户无需摆弄分片。 传统上&#xff0c;用户会更改数据流的分片配置&#xff0c;以处理各种工作负载并充分利用可用资源。…

TcpServer 服务器优化之后,加了多线程,对心跳包进行优化

TcpServer 服务器优化之后&#xff0c;加了多线程&#xff0c;对心跳包进行优化 TcpServer.h #ifndef TCPSERVER_H #define TCPSERVER_H#include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <vector> #include <map> #…

python进阶-05-利用Selenium来实现动态爬虫

python进阶-05-利用Selenium来实现动态爬虫 一.说明 这是python进阶部分05&#xff0c;我们上一篇文章学习了Scrapy来爬取网站&#xff0c;但是很多网站需要登录才能爬取有用的信息&#xff0c;或者网站的静态部分是一个空壳&#xff0c;内容是js动态加载的,或者人机验证&…

Linux —— vim 编辑器

一、什么是vim vim是一个功能强大、高度可定制的文本编辑器。以下是对vim编辑器的具体介绍&#xff1a; 历史背景&#xff1a;vim最初由Bram Moolenaar在1991年开发&#xff0c;作为vi编辑器的增强版&#xff0c;增加了许多新的特性和改进。它继承了vi的基本编辑功能和键盘快捷…

高效率同步降压转换器 - YB2416D: 实现快速充电和高效能供电的利器

概述: YB2416是一款输入耐压超过40V&#xff0c;在4.5V~30V输入电压条件下正常工作&#xff0c;并且能够实现精确恒压以及恒流的同步降压型DC-DC转换器。 内部集成80m2的上管和40m2的下管&#xff0c;无需外部肖特基二极管&#xff0c;可连续输出3A电流。输出3A电流时系统转换…

Repo管理

文章目录 前言Repo介绍清单仓库清单仓库的组成 初始化Repo同步远程仓库Repo实际应用 前言 我们知道&#xff0c;Git是用来管理某一个仓库&#xff0c;那当一个项目用到了多个仓库时&#xff0c;怎么来同步管理这些仓库呢&#xff1f;这个时候就可以引入Repo管理。 Repo介绍 …

神经网络的起源与工作原理

神经网络起源&#xff1a;一个生物神经网络是由一组化学上相连或功能上相关的神经元组成。一个神经元可能与许多其他神经元相连&#xff0c;网络中的神经元和连接的总数可能很广泛。连接&#xff0c;称为突触&#xff0c;通常是从轴突到树突形成的&#xff0c;尽管树突和其他连…

Qwen2.5-7B-Instruct vLLM 部署调用

Qwen2.5-7B-Instruct vLLM 部署调用 vLLM 简介 vLLM 框架是一个高效的大语言模型推理和部署服务系统&#xff0c;具备以下特性&#xff1a; 高效的内存管理&#xff1a;通过 PagedAttention 算法&#xff0c;vLLM 实现了对 KV 缓存的高效管理&#xff0c;减少了内存浪费&…

解决 Mac(M1/M2)芯片,使用node 14版本

前言 nvm 在安装 Node.js v14.21.3 时&#xff0c;报错&#xff1a; nvm install 14 Downloading and installing node v14.21.3... Downloading https://nodejs.org/dist/v14.21.3/node-v14.21.3-darwin-arm64.tar.xz... curl: (56) The requested URL returned error: 404Bin…

TesseractOCR-GUI:基于WPF/C#构建TesseractOCR简单易用的用户界面

前言 前篇文章使用Tesseract进行图片文字识别介绍了如何安装TesseractOCR与TesseractOCR的命令行使用。但在日常使用过程中&#xff0c;命令行使用还是不太方便的&#xff0c;因此今天介绍一下如何使用WPF/C#构建TesseractOCR简单易用的用户界面。 普通用户使用 参照上一篇教…

【ETCD】【源码阅读】configurePeerListeners() 函数解析

configurePeerListeners 是 ETCD 的一个核心函数&#xff0c;用于为集群中节点之间的通信配置监听器&#xff08;Peer Listener&#xff09;。这些监听器主要负责 Raft 协议的消息传递、日志复制等功能。函数返回一个包含所有监听器的列表。 函数签名 func configurePeerList…

uniapp改成用vue起项目

目的&#xff1a;让项目按照vue的打包流程跑流水线 1.按照uniapp官网教程执行 2.执行第二条命令时报错 ERROR Failed to get response from true/vue-cli-version-marker 3.解决方式 报错可能跟yarn有关&#xff0c;然后切换成npm 找到自己本地电脑的这个文件 按照截图修…

【SH】微信小程序调用EasyDL零门槛AI开发平台的图像分类研发笔记

文章目录 微信小程序字符串字符串模板字符串拼接 上传图片编写JS代码编写wxml代码编写wxss代码 GET请求测试编写测试代码域名不合法问题 GET和POST请求测试编写JS代码编写wxml代码编写wxss代码 效果展示 微信小程序字符串 字符串模板 这是ES6引入的特性&#xff0c;允许你通过…