C++基础

简介: 学习C++的基础语法(建立在已有的C语言基础上)

1.C++关键字

C++总计63个关键字,C语言32个关键字

ps:下面

asm do if return try continue
auto double inline short typedef for
bool dynamic_cast int signed typeid public
break else long sizeof typename throw
case enum mutable static union wchar_t
catch explicit namespace static_cast unsigned default
char export new struct using friend
class extern operator switch virtual register
const false private template void true
const_cast float protected this volatile while
delete goto reinterpret_cast

只是看一下C++有多少关键字,不对关键字进行具体的讲解。后面再具体分析一下。

2.命名空间

在C/C++中,访问变量,都是默认查找规则。先在局部找,再全局找。

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

说白了,就是同一个项目组里面,如果负责项目A的人和负责项目B的人,都想要某个变量名称,但在代码合并后,那肯定会有变量名相同导致的bug,因此,在C++中,给这些全局变量,围上了一道墙--namespace,需要访问这些变量的时候,就得通过这道墙的大门了。

2.1 命名空间的定义

①定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。

#include<iostream>namespacetest{
inta=10;
}
intmain()
{
inta=20;
std::cout<<a<<std::endl;
std::cout<<test::a<<std::endl;
return0;
}

image.gif

②命名空间可以嵌套命名空间。

#include<iostream>namespacetest1{
inta;
intb;
intadd(intleft, intright)
    {
returnleft+right;
    }
namespacetest2    {
intc;
intd;
intsub(intleft, intright)
        {
returnleft-right;
        }
    }
}
intmain()
{
test1::a=10;
test1::b=20;
intsum=test1::add(2, 5);
test1::test2::c=15;
test1::test2::d=25;
intSub=test1::test2::sub(10, 3);
std::cout<<test1::a<<std::endl;
std::cout<<test1::b<<std::endl;
std::cout<<sum<<std::endl;
std::cout<<test1::test2::c<<std::endl;
std::cout<<test1::test2::d<<std::endl;
std::cout<<Sub<<std::endl;
return0;
}

image.gif

③同一个工程项目里面可以有多个相同名称的命名空间,编译器会把它们合在一块。


namespacetest1{
inta;
intb;
intadd(intleft, intright)
    {
returnleft+right;
    }
namespacetest2    {
intc;
intd;
intsub(intleft, intright)
        {
returnleft-right;
        }
    }
}
namespacetest1{
intf;
intk;
}

image.gif

一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限在这个命名空间中。

2.2 命名空间的使用

那么,该如何使用定义的空间成员呢?

其实上面的代码已经说明了一部分了。

使用方法有三种:

①加上命名空间的名称和作用域限定符    如上面的:  test1 :: a;   test1就是命名空间的名称,::就是作用域限定符。

②使用using,将命名空间里面的某个成员引入。也就是说,把某个成员赶出这道墙,这样,就相当于普通的全局变量,谁都可以直接使用。

usingtest1::b;
intmain()
{   
std::cout<<b<<std::endl;
std::cout<<test1::b<<std::endl;
return0;
}

image.gif

③使用using namespace将命名空间名称引入。其实就是,将test1的围墙给拆了,里面的内容相当于普通的全局变量,谁都可以用。

usingnamespacetest1;
intmain()
{
std::cout<<b<<std::endl;
std::cout<<test1::b<<std::endl;
return0;
}

image.gif

3.C++输入&输出

#include<iostream>//std是C++标准库的命名空间名,C++将标准库的定义实现都放到了这个命名空间中usingnamespacestd;
intmain()
{
cout<<"hello world"<<endl;
return0;
}

image.gif

使用说明:

1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件

以及按命名空间使用方法使用std。

2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<

iostream >头文件中。

3. <<是流插入运算符,>>是流提取运算符。

4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。

C++的输入输出可以自动识别变量类型。

5. 实际上cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载等知识。这会在往后的学习,可以更加的深入去学习和理解。

温馨提示:

1. 在日常练习中,可以直接using namespace std,这样就很方便。

2. using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对

象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模

大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 +

using std::cout展开常用的库对象/类型等方式。因为,这跟前面提到了,使用了using namespace std展开后,里面的内容,就想到全部变成了普通的全局变量之类的东西,很容易出bug。因为std这道墙被拆了!

4.缺省参数

4.1 缺省参数的概念

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实

参则采用该形参的缺省值,否则使用指定的实参。

#include<iostream>voidfun(inta=10)
{
std::cout<<a<<std::endl;
}
intmain()
{
fun();
fun(2);
return0;
}

image.gif

4.2 缺省参数的分类

①全缺省参数

#include<iostream>voidfun(inta=1,intb=2,intc=3)
{
std::cout<<a<<" ";
std::cout<<b<<" ";
std::cout<<c<<std::endl;
}
intmain()
{
fun();
fun(10);
fun(10, 20);
fun(10, 20, 30);
return0;
}

image.gif

②半缺省参数

voidfun(inta ,intb=2,intc=3)
{
std::cout<<a<<" ";
std::cout<<b<<" ";
std::cout<<c<<std::endl;
}

image.gif

说明:

半缺省参数必须从右往左依次给出,中间不能隔着给。                                                                    缺省参数不能同时在函数的声明和定义中出现。因为如果恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值,因此,如果一个函数有声明和定义,一般把缺省参数给在声明上。定义的时候,只需写出数据类型和变量名。

缺省值必须是常量或者全局变量。

5.函数重载

5.1 函数重载的概念

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重

载了。

比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个

是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!

所谓重载,就是有多种意思。

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。


#include<iostream>intadd(intx, inty)
{
returnx+y;
}
doubleadd(doublex, doubley)
{
returnx+y;
}
intmain()
{
std::cout<<add(1, 2) <<std::endl;
std::cout<<add(1.1, 2.2) <<std::endl;
return0;
}

image.gif

#include<iostream>//1.参数类型不同的函数重载intadd(intx, inty)
{
returnx+y;
}
doubleadd(doublex, doubley)
{
returnx+y;
}
//2.参数个数不同的函数重载voidf()
{
std::cout<<"f()"<<std::endl;
}
voidf(inta)
{
std::cout<<"f(a)"<<std::endl;
}
//3.顺序不同是指参数的类型的顺序不同voids(inta, charb)
{
std::cout<<"s(int a,int b)"<<std::endl;
}
voids(charb, inta)
{
std::cout<<"s(char b,int a)"<<std::endl;
}
//不能这样voidf(inta, intb)
{
}
voidf(intb, inta)
{
}

image.gif

5.2C++支持函数重载的原理--名字修饰

这里只是简单说说,有个相对的了解。往后会在深入学习C++时,会深入学习这方面的。

在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参
数类型信息添加到修改后的名字中

E53(M[E1AE]HC[EMHBJ$T`D.png

 

对于函数重载和缺省参数的结合:

#include<iostream>voidf()
{
std::cout<<"f()"<<std::endl;
}
voidf(inta=0, charb=1)
{
std::cout<<"f(int a,char b)"<<std::endl;
}
intmain()
{
f(10);
f(10, 20);
f();//歧义,二义性!!return0;
}

image.gif

上面代码中,是成立函数重载和缺省参数的,但是呢,因为一个有参一个无参,在调用是,会产生二义性。

intf(inta, intb)
{
std::cout<<"f(int a,int b)"<<std::endl;
return0;
}
charf(intb, inta)
{
std::cout<<"f(int b,int a)"<<std::endl;
return'a';
}
intmain()
{
f(1,1);
f(2,2);
return0;
}

image.gif

上面这种情况,因为无法确定返回值,而且参数的数据类型是相同的,所以无法构成函数重载!

6.引用

6.1 引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。

比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。

9K(M76H_~NZQ)8@QWYB3(2D.png

形式:类型& 引用变量名(对象名) = 引用实体;


intmain()
{
inta=10;
int&ra=a;//引用int&x=a;
int&y=x;
//它们都是a的别名。x++;//a = 11;y++;//a = 12;a++;//a = 13;std::cout<<a<<std::endl;
return0;
}

image.gif

注意:引用类型必须和引用实体是同种类型的

再来看个例子:

voidswap(int&m, int&n)
{
inttemp=m;
m=n;
n=temp;
}
intmain()
{
intcc=1, dd=2;
swap(cc, dd);//不用传地址了return0;
}

image.gif

6.2 引用特性

1. 引用在定义时必须初始化

2. 一个变量可以有多个引用

3. 引用一旦引用一个实体,再不能引用其他实体

 

voidTestRef()
{
inta=10;
// int& ra; // 该条语句编译时会出错int&ra=a;
int&rra=a;
printf("%p %p %p\n", &a, &ra, &rra);
}

image.gif

6.3 常引用

一般来说,引用都会加上const,为什么呢?那什么时候加const,什么时候不加呢?

我们先来看看引用的场景。

6.4 引用的场景

我先来补充一点:权限的放大、缩小和平移。

什么是权限?就是某个变量、数据可读可写,或只可读或只可写。

权限的平移:a的权限是可读可写,然后,ra没有加上const,也跟a的权限一样,可读可写。

inta=10;
//权限的平移int&ra=a;

image.gif

权限的放大:这个做法是不允许,因为,原本,对a,我只能读,但是我引用后,却想要可读可写。就好比如,我有一台手机,我用的时候小心翼翼的,爱护着使用。但是我借给某个人后,那个人又是拍又是摔的,能允许吗?

constinta=10;
int&ra=a;

image.gif

权限的缩小:原本,对a是可读可写,引用后,只可读。这是允许的。我的手机,我自己本来就又摔又拍,然后给某人用,他小心翼翼地爱护着,怕弄坏了要赔给我。这是允许的。

inta=10;
constint&ra=a;

image.gif

了解了const和不加const的权限后,我们接下来看看:

①做参数:

voidSwap(int&left, int&right)
{
inttemp=left;
left=right;
right=temp;
}

image.gif

在做参数的时候,如果需要修改参数的值,则不加const。那么,我们传进去的参数,也不能加上const,因为会使权限放大。

其实做参数有几种情况:

       ※做参数时,直接引用,不加const,那么,只有a能传进去,b和ra不能,因为它们都加const修饰了,如果传进去,那就是权限放大了,我不能让你摔我的宝贝手机!

voidFunc(int&x)
{
}
intmain()
{
inta=0;
constint&b=0;
constint&rra=a;
Func(a);
Func(b);
Func(rra);
return0;
}

image.gif

       ※做参数时,加const修饰,那么不论是a还是b,rra,都能传入。

voidFunc(constint&x)
{
}
intmain()
{
inta=0;
constint&b=0;
constint&rra=a;
Func(a);
Func(b);
Func(rra);
return0;
}

image.gif

引用做参数的好处:减少拷贝,提高效率。在输出型的参数时,形参修改了,实参也修改了。

②做返回值:

这里再补充两个点:第一个点就是,函数在返回值时,函数栈帧销毁后,会创建一个临时变量,用来接收这个返回值,然后再传给调用函数的那个变量。而这个临时变量,具有常性!其实不止是函数返回值会创建临时变量,在数据类型转换的时候,也是这样转换的。

J4%E(OWW5E$%}T62{(4E6IJ.png$5BTK%A()G])A6%HHSO$3ID.png

根据这个原理,我们来分析一下下面这段代码:

intCount()
{
intn=0;
n++;
// ...returnn;
}
intmain()
{
constint&b=10;
doubled=12.34;
cout<< (int)d<<endl;
inti= (int)d; // 可以//int& ri = d; // 不可以constint&ri=d; // 可以cout<<ri<<endl;
//int& ret = Count();constint&ret=Count();
return0;
}

image.gif

第一个:b的数据类型是int&,如果直接给10,不给const的话,就会报错。为啥?因为10是一个常量,而b的类型是引用,代表着是10这个的别名,因此,b也得是个常量,所以需要加上const。

第二个:double类型的d,转换成int,不是将d的数据类型转换成int,而是在执行(int)d的时候,创建了临时变量,这个临时变量的类型是int,然后再传回给接收这个值的变量或者输出。而不需要const的原因,是变量本来就能接收常量,比如:int a = 10,但是不能int& a = 10,因为int是创建一个变量,int&是引用,需要看看引用的是常量还是变量,如果是int& b = a,那么就不需要加const,因为a是变量,而int&接收了变量,b是a的别名,不需要常性。

第三个:int& ri = d是错误的,加上const才是对的,这个不用再重复说了,因为临时变量是常量。。。

第四个:函数int Count();int& ret = Count();是错误的,因为,这个函数返回n时,需要创建临时变量,是个常性,int&引用常量,得加const。

第二个点是空间销毁,意味着:空间虽然还在,但是使用权不在我们,我们存进去的数据不被保护,虽然还能访问,但是访问到的数据,是个不确定值!因此,什么时候需要返回int&,还是int?

基于上面两点,我们看下面的分析:

       ※做返回值时,没有使用引用:从上面的分析可知,为啥没加const不行,就是因为返回来的是具有常性的临时变量,int&引用的是常量,需要加const。

intCount1()
{
intn=0;
cout<<&n<<endl;
n++;
// ...returnn;
}
intmain()
{
intret1=Count1();
//int& reet1 = Count1();//不可以constint&rret1=Count1();//可以return0;
}

image.gif

        ※做返回值时,使用了引用:使用了引用的返回值,没有创建临时变量 。如下的代码:因为返回的是一个n的引用,因为n的类型是int,那么返回的就是n的别名,也是int类型,那么,如果是int n = 10,那么可以有int& rret2 = n,权限的平移。所有,不需要加const!

   

int&Count2()
{
staticintn=0;
cout<<&n<<endl;
n++;
// ...returnn;
}
intmain()
{
intret2=Count2();
int&rret2=Count2();
return0;
}

image.gif

由于函数的栈帧销毁后,会将里面的内容也销毁,因此,在决定是否使用常引用来做返回值,就需要考虑以下问题:

出了函数作用域,返回变量不存在了,不能引用返回,因为引用返回的结果是未定义的。            出了函数作用域,返回变量还在,能够使用引用返回。

而使用引用返回的好处就是:减少拷贝,提高效率。还能修改返回值。

6.5传值、传引用效率比较

传引用的效率比较高,不管是引用返回值还是引用参数

6.6引用和指针的区别

在语法上,引用是没有开辟新空间的,它跟引用的实体共用一个空间。而指针是需要开辟空间,来存放目标变量的指针

在底层,其实引用也是有开辟新空间的,因为引用是按照指针方式来实现的。

使用反汇编代码就能看出来:

[3G)0Z28`2DI%(235}1CZI3.png

最后,引用跟指针的区别:

引用和指针的不同点:

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

2. 引用在定义时必须初始化,指针没有要求

3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
一个同类型实体

4. 没有NULL引用,但有NULL指针

5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)

6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

7. 有多级指针,但是没有多级引用

8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

9. 引用比指针使用起来相对更安全

7.内联函数

7.1概念

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。如下图,如果加入了inline,那么在汇编中,函数的没有call指令来创建函数栈帧,在编译期间编译器会用函数体替换函数的调用。

I%7MJOESN$88{W~}O}PZ]YL.png

GSAHU48AYX2PGR%W2S@1[`2.png

 

7.2特性

1. inline是一种以空间换时间(这里的空间是指编译出来的可执行程序的大小)的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率.

2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

C++prime》第五版关于inline的建议:

S(LEAKT45_FBN0@H(T9EL~C.png

3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址

了,链接就会找不到。

注意:只要函数是内联函数,无论编译器采取不采取inline的修饰,链接的时候在符号表里面都不会有这个函数的地址,也就导致无法找到这个函数,导致声明和定义的使用错误。

问:

宏的优缺点?

优点:

1.增强代码的复用性。

2.提高性能。

缺点:

1.不方便调试宏。(因为预编译阶段进行了替换)

2.导致代码可读性差,可维护性差,容易误用。

3.没有类型安全的检查 。

C++有哪些技术替代宏?

1. 常量定义 换用const enum

2. 短小函数定义 换用内联函数

 

8.auto关键字

8.1 类型别名思考

随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在::

1. 类型难于拼写
2. 含义不明确导致容易出错

虽然使用typedef给类型取别名确实可以简化代码,但是typedef有会遇到新的难题:

typedefchar*pstring;
intmain()
{
constpstringp1; // 编译成功还是失败?constpstring*p2; // 编译成功还是失败?return0;
}

image.gif

解释:

在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的类型。然而有时候要做到这点并非那么容易,因此C++11给auto赋予了新的含义。

8.2 auto简介

++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

intTestAuto()
{
return10;
}
intmain()
{
inta=10;
autob=a;
autoc='a';
autod=TestAuto();
cout<<typeid(b).name() <<endl;
cout<<typeid(c).name() <<endl;
cout<<typeid(d).name() <<endl;
//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化return0;
}

image.gif

注意:

使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型

 

8.3 auto的使用细则

1. auto与指针和引用结合起来使用

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

intmain()
{
intx=10;
autoa=&x;
auto*b=&x;
auto&c=x;
cout<<typeid(a).name() <<endl;
cout<<typeid(b).name() <<endl;
cout<<typeid(c).name() <<endl;
*a=20;
*b=30;
c=40;
return0;
}

image.gif

2. 在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

voidTestAuto()
{
autoa=1, b=2;
autoc=3, d=4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同}

image.gif

8.4 auto不能推导的场景

1. auto不能作为函数的参数

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导voidTestAuto(autoa)
{}

image.gif

2. auto不能直接用来声明数组

voidTestAuto()
{
inta[] = {1,2,3};
autob[] = {4,5,6};
}

image.gif

9.基于范围的for循环

9.1 范围for的语法

在C++98中如果要遍历一个数组,可以按照以下方式进行

 

voidTestFor()
{
intarray[] = { 1, 2, 3, 4, 5 };
for (inti=0; i<sizeof(array) /sizeof(array[0]); ++i)
array[i] *=2;
for (int*p=array; p<array+sizeof(array)/sizeof(array[0]); ++p)
cout<<*p<<endl;
}

image.gif

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

voidTestFor()
{
intarray[] = { 1, 2, 3, 4, 5 };
for(auto&e : array)
e*=2;
for(autoe : array)
cout<<e<<" ";
return0;
}

image.gif

注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环

 

9.2 范围for的使用条件

1. for循环迭代的范围必须是确定的

对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。

注意:以下代码就有问题,因为for的范围不确定:因为传数组进去,传的就是首元素的地址,然而这是没有范围锁定的。

voidTestFor(intarray[])
{
for(auto&e : array)
cout<<e<<endl;
}

image.gif

2. 迭代的对象要实现++和==的操作

以后会提到这点

 

10.指针空值--nullptr

10.1 C++98中的指针空值

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:

voidTestPtr()
{
int*p1=NULL;
int*p2=0;
// ……}

image.gif

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif#endif

image.gif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:

voidf(int)
{
cout<<"f(int)"<<endl;
}
voidf(int*)
{
cout<<"f(int*)"<<endl;
}
intmain()
{
f(0);
f(NULL);
f((int*)NULL);
return0;
}

image.gif

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。可以看到下面的结果,f(NULL)调用了第一个函数


在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器
默认情况下将其看成是一个整形常量
如果要将其按照指针方式来使用,必须对其进行强转(void
*)0

 

因此,C++11引用了nullptr,解决了上面的问题。

注意:

1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。

2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。

3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

END~

       

相关文章
|
网络架构 Windows
基础修炼
基础修炼
61 0
|
人工智能
基础练习-3
基础练习-3
98 0
|
1月前
|
Kubernetes 监控 调度
k8s基础
k8s基础
41 0
|
6月前
|
关系型数据库 ice RDS
数字电子技术基础 2
数字电子技术基础
37 0
|
6月前
数字电子技术基础4
数字电子技术基础
30 0
|
6月前
|
传感器 编解码 C++
C++视频基础
C++视频基础
|
存储 编译器 C++
C++语法基础(六)
C++语法基础(六)
C++语法基础(六)
|
SQL 网络协议 网络安全
一、基础篇
一、基础篇
269 0
一、基础篇
|
存储 C#
C#基础
C#基础
93 0
|
图形学 Windows
GDI+基础
GDI+的核心是 Graphics 对象,Graphics 类定义了绘制和填充图形对象的方法和属性。Graphics 类的属性(字段)很多,具体可参见 MSDN。Graphics 类的方法分为三类:绘制、填充及其他。
745 0
GDI+基础
下一篇
无影云桌面