做团购网站,网络公司名字大全寓意,池州专业网站建设,wordpress 多页auto、decltype自动类型推导
auto并不代表实际的数据类型#xff0c;且auto并不是在任何场合下都能推导出变量的实际类型#xff0c;使用auto必须初始化。 auto 变量名 变量值#xff1b; 当变量不是指针或引用类型时#xff0c;推导的结果中不会保留const、volatile关键…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推出模板参数
templatetypename T
struct Test {T a};
int func()
{Testdoublet;Testautot1 t;//error 无法推出模板类型
}
相比于auto用于推导变量类型decltype则用于推导表达式类型这里只用于编译器分析表达式的类型表达式实际不会进行运算。 decltype(表达式) const int i 1;decltype(i) b 1;//b是const intint 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 ab;}); //作为匿名函数使用 表示形式 具体语法 解释 举例 能不能改变int a100; [ ](){return a 10; } //错误 常函数a是值传递不可以改变[ ](){return a 10;} //可以 a是引用传递a会被改写成10[ ]()mutable{return a 10;} //可以 mutable 可变副本a会被改写成10外面a不变[ ]()mutable{return a 10:} //可以 a是引用传递mutable 可变a会被改写成10 #includeiostream
using namespace std;class AA
{
public:AA(){auto f0 [this](){return this;};coutf0()endl;}
};int val1 10;
int main()
{auto f1 [](){return val1 100;}; //全局的变量不用捕获可以直接使用coutf1()endl; //100coutval1endl; //100int j 5;auto f2 [j](){return j 10;};auto f3 [j](){return j;}; //先后执行会有不同coutf2()endl; //10coutjendl; //10//修改的是副本auto f4 [j]()mutable{return j 100;};auto f5 [j]()mutable{return j 200;};coutf4()endl; //100coutjendl; //10coutf5()endl; //200coutjendl; //10auto f6 [j]()mutable{return j 300;};coutf6()endl; //300coutjendl; //300AA a;return 0;
}智能指针 头文件#includememory auto_ptr已在C11中弃用 创建方式 有参构造reset()方法赋值 存在的问题指针的所有权交出的太随意交出后原指针为空后续通过该指针调用会出问题。
unique_ptr
唯一所有权智能指针在auto_ptr的基础上禁止拷贝构造与赋值通过move()对使用权进行转移避免auto_ptr在用户无意识情况下将指针的所有权交出。 使用场景单例模式
shared_ptr #include iostream
#includevector
#includestring
#includememory
using namespace std;class AAAA{
private:string s;
public:AAAA( const string str){s str;coutAAAA: s endl;}~AAAA(){cout~AAAA endl;}void say(){coutsay: s endl;}
};int main() {shared_ptrAAAA sp1( new AAAA(shared_ptr1));shared_ptrAAAA sp2;sp2.reset( new AAAA(shared_ptr2));shared_ptrAAAA sp3;sp3 make_shared AAAA(share_ptr3);{shared_ptrAAAAsp4;sp4sp3; //指向同一个 共享cout sp4.use_count() endl;//2}sp3.get()-say();//say:share_ptr3sp3-say();//say:share_ptr3coutsp3.use_count()endl;//查看引用计数 1sp3.reset();//主动回收空间return 0;
}存在的问题
1、循环引用
什么是循环引用举例 解释此时spc.use_count()为2spb.use_count()也为2走到{ }的结尾处对pcpb回收此时引用计数都变为1空间无法回收这就是循环引用。
2、多线程下的线程安全问题
为了保证空间正确回收引用计数1或-1是线程安全的但关于指针指向空间的使用是非线程安全所以在多线程下使用这个空间要加锁。 weak_ptr 同时在weak_ptr销毁时对应空间的引用计数也不会减1。
weak_ptr弱引用计数智能指针它的提出主要是解决shared_ptr中循环引用的问题。
怎么解决举例 解释将spb或spc由shared_ptr改成weak_ptr这里我们改spb。此时spc.use_count()为1spb.use_count()为2回收pc、pb后spc.use_count()为0回收其空间当该空间回收后该空间的spc对象也就不存在了这时spb.use_count()也由1变为0对应的空间也得到了回收。 可以使用 lock()方法获取与weak_ptr捆绑的shared_ptr。lock() 方法会尝试升级 weak_ptr 到一个 shared_ptr。如果 weak_ptr 所指向的对象仍然存在即没有被销毁lock() 方法会成功并返回一个 shared_ptr如果对象已经被销毁它会返回一个空的 shared_ptr。weak_ptr不能直接调用函数如果想调用需要先获得与weak_ptr捆绑的shared_ptr再通过shared_ptr进行调用。expire()方法用于检查 weak_ptr 是否指向一个有效的 shared_ptr。如果 weak_ptr 所指向的对象已经被销毁即所有与之关联的 shared_ptr 都已被释放expire() 将返回 true表示 weak_ptr 失效。 举例
#include iostream
#include memoryclass MyClass {
public:void doSomething() {std::cout Doing something std::endl;}
};int main() {std::shared_ptrMyClass sp(new MyClass());std::weak_ptrMyClass wp(sp); // 从 shared_ptr 创建 weak_ptr//std::weak_ptrMyClass wp sp;// 模拟 shared_ptr 被销毁的情况sp.reset();if(wp.expired()){std::coutweak_ptr失效std::endl;//weak_ptr失效}// 尝试通过 weak_ptr 获取 shared_ptrstd::shared_ptrMyClass 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;
}
表示形式 举例 总结左值引用不可引用右值右值引用也不可引用左值但可以引用移动的左值。
作用 引用临时对象延长临时对象生命周期实现移动语义和完美转发消除两个对象交互时不必要的对象拷贝能够更简洁明确地定义泛型函数。(更准确的传参和调用函数) 解释 转移语义可以将资源(堆系统对象等)从一个对象转移到另一个对象这样能够减少不必要的临时对象的创建、拷贝以及销毁能够大幅度提高C应用程序的性能 举例
#includeiostream
using namespace std;#includevectorvectorint getVec(vectorint vec)
{coutfun::vec addr:vec.front()endl; //fun::vec addr:0x1e1275d0return vec;
}vectorint getVec2(vectorint vec)
{coutfun::vec2 addr:vec.front()endl; //fun::vec2 addr:0x1e1275d0return move(vec);
}int main() {//转移语义vectorint vec {1,2,3,4,5,6};coutvec addr:vec.front()endl; //vec addr:0x1e1275d0vectorint vec1 getVec(vec);coutvec1 addr:vec1.front()endl; //vec1 addr:0x1e1285f8vectorint vec2 getVec2(vec);coutvec2 addr:vec2.front()endl; //vec2 addr:0x1e1275d0coutvec items:endl;for(auto num : vec){coutnum ; //空}coutendl;for(auto num : vec2){coutnum ; //1 2 3 4 5 6 }coutendl;return 0;
}右值引用可以实现精确传递 精确传递适用场景需要将一组参数原封不动的传递给另一个函数。精确传递就是在参数传递过程中所有属性和参数值都不能改变。在泛型函数中这样的需求非常普遍。 举例
#includeiostream
using namespace std;template typename T
void forward_value(const T val)
{coutforward_value(const T val)endl;
}template typename T
void forward_value(T val)
{coutforward_value(T val)endl;
}int main() {//精准传参int a 0;const int b 1;forward_value(a); //intforward_value(b); //const intforward_value(2); //const intreturn 0;
}
由上述代码可知常引用以及常量匹配的都是const int那么我们再加上右值引用会发生什么呢
#includeiostream
using namespace std;template typename T
void forward_value(const T val)
{coutforward_value(const T val)endl;
}template typename T
void forward_value(T val)
{coutforward_value(T val)endl;
}template typename T
void forward_value(T val)
{coutforward_value(T val)endl;
}int main() {//精准传参int a 0;const int b 1;forward_value(a); //intforward_value(b); //const intforward_value(2); //intreturn 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_castA2*(pa); // 转换可以但是后面使用就出问题了 pa2 - fun2(); 注 A是B的父类B-A 继承中对象的向上转换A-B 向下转换不相关的不能转换student*与int*之间不能转换char*与int*之间也不能(因为不安全)int - int* 不可以int*-int 也不可以 相当于限制版的强制类型转换 代码举例
#includeiostream
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_castint*(pc);//char*-int* 不允许void* lpvoid static_castvoid*(pc);a static_castint(c);//student* ps static_caststudent*(p);//int*-student* 不允许//p static_castint*(st);//student*-int* 不允许//a static_castint(p);//int*-int 不允许//p static_castint*(a);//int-int* 不允许A2* pa2 (A2*)(new A1);//子类指针默认无法指向父类对象除非强转//继承关系中对象的向上转换A1* pa1 static_castA1*(pa2);pa1 new A2;//继承关系中对象的向下转换pa2 static_castA2*(pa1);A3* pa3 static_castA3*(pa1);if(pa3){coutpa3 is validendl;//pa3会取到值但后续如果执行A3里面的函数会有问题}//C语言强制类型转换student* ps2 (student*)(p);p (int*)st;}return 0;
}dynamic_cast 动态转换 只适用于继承上的两个类之间转换继承的向上转换效果等同于static_cast继承的向下转换会进行检验向下转换安全(即父类指针要转换为子类指针恰巧这个父类指针指向这个子类对象可以得到对应结果)向下转换不安全(即父类指针要转换为子类指针恰巧这个父类指针不指向这个子类对象返回结果为空)向下转换父类中必须包含虚函数(static_cast 没有这个要求)特殊情况 向下转换安全(即父类指针要转换为子类指针恰巧这个父类指针指向这个子类的子类)通俗来说就是爷爷指向孙子此时爷爷想转化为父亲。 代码举例
A1、A2、A3、A4之间关系图 #includeiostream
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_castA1*(pa2);pa1 new A2;//继承关系中对象的向下转换pa2 dynamic_castA2*(pa1);A3* pa3 dynamic_castA3*(pa1);if(pa3){coutpa3 is validendl;}else{coutpa3 is invalidendl;//pa3 is invalid}//特殊情况向下转换安全父类指针要转换为子类指针恰巧这个父类指针指向这个子类的子类pa1 new A4;pa2 dynamic_castA2*(pa1);if(pa2){coutpa2 is validendl;//pa2 is valid}else{coutpa2 is invalidendl;}pa1 new A2;A4* pa4 dynamic_castA4*(pa1);if(pa4){coutpa4 is validendl;}else{coutpa4 is invalidendl;//pa4 is invalid}//交叉转换//pa3 static_castA3*(pa2); //相当于A1转A2,A2又转A3静态转换不可以pa3 dynamic_castA3*(pa2); //动态转换可以}return 0;
}为什么要用dynamic_cast 对于动态转换中继承的向下转换,怎么进行安全检验的 根据虚函数的入口地址进行检验如果虚函数的入口地址相同那么是安全的。 static_cast与dynamic_cast区别? static_cast 支持相关数据类型转化dynamic_cast不支持对于继承关系中的转换,static_cast可以向上和向下转换,dynamic_cast向上转换与static_cast一致,向下转换,父类要有虚函数,并且存在检验机制static_cast不支持交叉转换,只能是向上下dynamic_cast可以交叉转换 const_cast 去常转换 int* pconst_castint*( 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 midmax midmin ? 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使用代码
#includeiostream
using namespace std;int add(int a,int b)
{return a b;
}
//add 可调用对象
//function 函数封装器存储可调用对象
//bind 将可调用对象和部分参数绑定在一起
#includefunctionalint main()
{std::functionint(int,int)f add;coutres:f(3,4)endl;// res:7//在调用之前 将参数与函数指针捆绑 然后使用function存储//因为已经给定参数 所以类型参数列表int()中为空//因为捆绑的时候已经传完参数 所以调用时不需要传参std::functionint()f1 std::bind(add,3,4);coutres:f1()endl;// res:7//当捆绑时不知道需要传递的参数时可以使用std::placeholders::_1 进行参数占位std::functionint(int ,int)f2 std::bind(add,std::placeholders::_1,std::placeholders::_2);coutres:f2(3,4)endl;// res:7return 0;
}捆绑普通函数
#includeiostream
using namespace std;int add(int a,int b)
{return a b;
}#includefunctional//加减乘除都可以实现计算
int getResult(std::functionint(int,int)f)
{int a 0,b 0;cin a b;return f(a,b);
}
//getResult() 是回调函数先将函数指针传入可能包含参数在需要的时候执行int main()
{std::functionint(int ,int)f std::bind(add,std::placeholders::_1,std::placeholders::_2);int res getResult(f);coutres:resendl;return 0;
}捆绑类成员函数
#includeiostream
using namespace std;class AA
{
public:int multi(int a,int b){return a * b;}
};#includefunctional//加减乘除都可以实现计算
int getResult(std::functionint(int,int)f)
{int a 0,b 0;cin a b;return f(a,b);
}int main()
{AA aa;//将类成员函数指针与对象的地址捆绑类中非静态成员函数第一个参数默认为this指针//然后multi还有两个参数 int intstd::functionint(int ,int)f std::bind(AA::multi,aa,std::placeholders::_1,std::placeholders::_2);int res getResult(f);coutres:resendl;return 0;
}捆绑静态成员函数
#includeiostream
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;}
};#includefunctional//加减乘除都可以实现计算
int getResult(std::functionint(int,int)f)
{int a 0,b 0;cin a b;return f(a,b);
}int main()
{std::functionint(int ,int)f std::bind(AA::div,std::placeholders::_1,std::placeholders::_2);int res getResult(f);coutres:resendl;return 0;
}使用function与bind有什么好处 可以在使用类成员函数时不依赖对象提前将对象地址绑定到函数指针上 chrono时间库
C11有了chrono时间库可以在不同系统中很容易的实现定时功能。
要使用chrono库需要#includechrono其所有实现均在std::chrono
chrono是一个模版库使用简单功能强大只需要理解三个概念duration、time_point、clock
#includeiostream
#includechrono
#includethread
using namespace std;int main()
{//时间间隔举例std::this_thread::sleep_for(std::chrono::milliseconds(2000));//std::this_thread::sleep_for(std::chrono::seconds(2));coutsleep finishendl;//this_thread代表当前线程 sleep_for休眠 参数传的是chrono的时间间隔//milliseconds 毫秒 seconds 秒/*chrono::millisecondsmilliseconds 类型别名typedef durationint64_t, milli milliseconds;typedef ratio1,1000 milli;ratio1,1000 分数 1/1000 因为要使用ms 所以是1s的1/1000chrono::duration 结构体 //模版参数 第一个是数值 第二个是单位std::chrono::milliseconds(2000)std::chrono::durationint64_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_castchrono::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_castchrono::milliseconds(d).count();coutdiff:diffendl;return 0;}时间间隔 duration chrono::milliseconds milliseconds 类型别名 typedef durationint64_t, milli milliseconds; typedef ratio1,1000 milli; ratio1,1000 分数 1/1000 因为要使用ms 所以是1s的1/1000 chrono::duration 结构体 //模版参数 第一个是数值 第二个是单位 std::chrono::milliseconds(2000) std::chrono::durationint64_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()函数将时间转换成字符串格式就可以很方便地打印当前时间了。