字面意思可以看到,nullptr指的是空指针的意思,在以前任意类型的指针可以直接用NULL赋值:char* p=NULL。然而实际上NULL在编译器内部是一个宏定义#define NULL 0。这就可能在某些情况下产生一些问题。
例如:假设有两个函数重载
void foo(int a){};///1 void foo(char*a){};///2
在main函数中调用foo(NULL);,显然该调用会去调用2函数,这有时候和我们的编写意图相违背。因此就有了nullptr关键字,就好像是为以前的语法打了补丁,nullptr表示空指针,本质上还是0,但是带了类型。
2,constexptrconstexptr表示常量表达式类型,不太常用,下面介绍其作用——
在以下代码中,p声明为一个常量5,foo()的返回值同样是一个常量5
const int p=5; const int foo(){return 5};
接下来使用这两个常量,会发现第一条语句合法,第二条语句非法
int p1 [p];//合法 int p2 =[foo()];//非法
原因就是静态声明数组的时候,[ ]内部需要一个常量表达式,而const int类型的函数返回值编译器不会认为是常量表达式,所以有了constexptr,constexptr int foo(){return 5};
不常用
1)stl中存在一些常见的已经封装好(开箱即食)数据结构相关的模板类,例如vector(动态数组),list(链表),stack(栈),queue(队列),map(hash表/红黑树)等。这些类通常都有一些最基本的 *** 作,例如:增加,删除,修改,遍历等等。关于这些模板类的其他 *** 作可见我的这篇博客开启你的leetcode刷题之旅之前必学(C++)——stl基本模板类的使用,Vectors,Lists,Stack,Queues
2)C++为了方便统一,采用了设计模式中的迭代器模式,也就是统一的提供了一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。我们一般对这些数据结构的遍历都可以无脑使用迭代器,而不关心内部存储的差异。stl迭代器中只有vector类型可以通过下标访问其元素,因为vector类型数据是连续存储的。当然也可以直接用迭代器访问vector。
下面演示一下list容器怎么用迭代器访问,其他都差不多。
listss; ss.push_back("hello1"); ss.push_back("hello2"); ss.push_back("hello3"); for (list ::iterator i = ss.begin(); i != ss.end(); i++) { cout << *i< 这就是迭代器的用法,迭代器到这里就介绍的差不多了,接下来介绍auto关键字。
3.2,auto关键字前面的代码中我们发现一个可以用于list类型容器的迭代器声明为list
::iterator i,实话说确实写起来比较麻烦,现在C++允许直接使用auto关键字做一个类型,为自动类型推导,例如上例中的list ::iterator i=ss.begin()可以改写为auto i=ss.begin(),因为ss.begin()的返回值本来就是list ::iterator类型的,所以C++看到auto i=ss.begin()语句就将auto关键字自动推导为list ::iterator类型的,编译器自动完成了自动类型的推导。这就是auto的作用。
扩展:
C++还吸收了python的语法,还可以这样遍历for (auto str : ss) { cout << str; }//这个auto自动推导为string类型4,委托构造与继承构造 4.1,委托构造委托构造一般不常用,不过可以了解一下:看下面的例子:这里base(int k) 委托构造了base()
class base { public: int a; int b; base() { a = 1; } base(int k):base() { b = k; } };下面声明对象base obj(2);,该对象声明以后我么应该可以一眼看出他对应的构造函数是base(int k),而base(int k)又委托构造了base(),所以该对象声明会调用两个构造函数,同时调试一下就可以发现,该声明会先调用base()构造,然后调用base(int k)构造函数。
4.2,继承构造子类继承了父类的变量,如果子类想要初始化继承自父类的变量的话必须自己写构造函数,如果想要直接用父类的构造函数怎么办?C++提供了这种语法——继承构造。看下面的例子观察语法。
class base { public: int a; int b; base() { a = 1; } base(int k):base() { b = k; } }; class subbase:public base{ public: using base::base;//声明使用父类构造 }接下来声明对象subbase objs(2)就会调用父类的构造函数base(int k)。
5,虚函数override以及类的final 5.1,虚函数override关键字override的意思是显式地声明某虚函数是个重载函数。为什么有这个语法,因为在某个场景下,例如父类声明了virtual void foo();,子类重写了void foo()那么子类的foo()方法也就默认认为他是虚函数,但有时候我们写了太多代码可能忘了父类还有一个foo是虚函数,子类就写了一个普通函数一不小心起了个名字同样叫foo,编译器不会提醒,我们就这样错了。编译器会认为子类这样写是重写了父类的foo虚函数。再一种情况就是如果父类的foo的virtual关键字一不小心删了,那么子类的foo方法同样就不是虚函数了。因此有了overfide关键字显式地声明该函数是虚函数重载即可解决我们可能遇到的一些粗心的问题。
5.2,类的final
子类的foo方法可写为void foo override(){};final就是最终的意思。
1,可以加在类的声明后面,表示该类是继承的最后一个类了,不允许被别的类继承。class subbase final:public base{ };//表示subbase类不允许被继承了2,可以加在虚函数的后面,表示该虚函数不允许被子类重写了。
virtual void foo() final{ }欢迎分享,转载请注明来源:内存溢出
评论列表(0条)