走进C++:C到C++的过渡

目录

什么是C++呢?

C++的发展史

多了一些吃前来很香的“语法糖”。

语法糖一:命名空间

命名空间有个强大的功能

如何使用

语法糖二:缺省参数

语法糖三:函数重载

语法糖四:引用        

引用传参

引用返回

引用和指针的不同点:

语法糖五:内联函数

内联函数相比于普通函数的特性

语法糖六:auto关键字(C++11)

auto的使用规则

语法糖七:基于范围的for循环(C++11)

范围for的语法

范围for的使用条件

语法八:指针空值nullptr(C++11)


什么是C++呢?

先官方的解释一下:

C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。

为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(objectoriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
 1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。

因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。

简而言之:

C++就是本贾尼大佬看不惯C语言,想要创造一门用起来更爽的语言,因此创造了C++。

同时C++也是包含C语言的,因此C语言的所有语法在C++都能使用。

看这位大佬的发量,就大体明白,C++也不是容易搞明白的,因此本文将带你利用C语言的基础,走进C++。

C++的发展史

虽然本贾尼是C++之父,但是C++的发展却不只是牺牲了这位大佬的“发际线”,当然还有其他大佬……

  1. C with classes(C++前身):引入了类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符重载等特性。
  2. C++1.0:增加了虚函数概念,支持函数和运算符的重载,以及引用和常量等。
  3. C++2.0:提供了面向对象编程的支持,包括保护成员、多重继承、对象的初始化和抽象类、静态成员等。
  4. C++3.0:进一步完善了面向对象特性,引入了模板技术来解决多重继承产生的二义性问题。
  5. C++98:作为第一个官方发布的C++标准版本,得到了广泛的支持,并引入了STL(标准模板库)。
  6. C++03:对语言特性进行了小幅修订,主要是为了减少多异性。
  7. C++05:引入了TR1,正式更名为C++0x,增加了许多新特性,如正则表达式、基于范围的for循环等。
  8. C++11:引入了许多革命性的新特性,如lambda表达式、auto类型推导、标准线程库等,使C++更像一门现代语言。
  9. C++14:修复了C++11中的漏洞,并增加了新的特性,如泛型lambda表达式、二进制字面常量等。
  10. C++17:在C++11的基础上做了改进,增加了19个新特性,包括static_assert()、Fold表达式等。
  11. C++20:这是自C++11以来最大的发行版,引入了模块、协程、范围、概念等特性,并对已有特性进行了更新。
  12. C++23:目前正在制定中,预计会进一步扩展和优化C++语言的功能。

总的来说,C++语言从最初的C with classes发展到现在的C++23,历经多次迭代和更新,不断丰富和完善其功能,使其成为了一门强大而灵活的高级编程语言。

为什么要学习C++

C++是目前市面上使用最多的语言之一,C/C++一直在语言界稳居前三。

对于操作系统、嵌入式开发、服务端开发、游戏开发,都需要大量的使用C++,因此学习C++非常有必要。

言归正传,下面进入C++的正式学习。

开篇就提到,C++相比于C语言是用起来很“爽”的语言,为什么真么说呢,主要是C++补充了C语言的一些语法,可以理解为:

多了一些吃前来很香的“语法糖”

语法糖一:命名空间

当我们写一些大型的C语言项目时,大量的变量命名常常会搞得我们头昏脑胀,不会命名、命名冲突,导致语法错误频出,C++便引入了第一个语法糖:命名空间

对于C语言的函数,变量的命名我们已经很熟悉,那么C++是如何命名的呢?答案是借助一个关键字:namespace。namespace顾名思义就是命名空间的意思,因此namespace可以创建一个命名空间。

举例:

namespace XXX
{int x = 0;double y = 0.0;
}

为了防止命名冲突,我们将变x、y放在命名空间XXX中。需要注意的是,一个独立的命名空间,花括号的尾部不能有分号!

命名空间有个强大的功能

在C++中,命名空间(namespace)是一种将代码组织成逻辑块的机制,它可以包含类、函数、变量、模板、类型别名等。命名空间的主要目的是提供一种组织代码的方式,以避免命名冲突,尤其是在大型项目中或者当多个库被一起使用时。

类、函数、变量、模板、类型别名,都可以包含在命名空间中。同时命名空间可以套用命名空间。

如何使用

有了命名空间之后,我们该如何使用命名空间的内容呢?

namespace Test
{int test = 10;int Add(int a, int b){return a + b;}
}

我们依靠的是一个操作符:域作用限定符 ::

为什么是域作用限定符呢?“域”即是区域,只要被花括号{ }括起来的空间,都可以看作一块域,访问命名空间,需要使用 ::限定符。

三大使用方式

以Test为例

方式一:将命名空间全部展开。

展开是一种授权,授权可以去对应的命名空间查找内部成员。具体实现为using namespace Test;


namespace Test
{int test = 10;int Add(int a, int b){return a + b;}
}using namespace Test;	//展开命名空间的所有元素int main()
{printf("%d\n", test);return 0;
}

test变量属于Test命名空间,当展开Test命名空间之后,就可以访问内部成员。

但是,将命名空间全部展开也是一种危险的行为

namespace Test
{
    int test = 10;

    int Add(int a, int b)
    {
        return a + b;
    }
}

int test = 81;

using namespace Test;

见此代码,当test函数在全局与Test中同时定义时,就会出现命名冲突。因此无论哪种展开方式(包含下面的方案),展开都不应该出现命名冲突。

方法二:将部分成员展开。

具体实现是using Test :: test;表示展开Test空间中的test变量。using + 空间名字 :: 成员名字。此时仍然可以直接访问test。

方法三:直接在使用处展开。

格式: 命名空间 :: 成员

namespace Test
{int test = 10;int Add(int a, int b){return a + b;}
}// ::域作用限定符int test = 81;int main()
{printf("%d\n", Test::test);return 0;
}

虽然出现两个test,但是在使用处直接引用,就可以限定去Test空间寻找test变量,而不是去全局。

在工程中,常用的符号一般采用第二种展开方式,不常用的用第三种。

对于C++,有一个标准命名空间,叫做std,常用的函数都在std中。

在C++中,输出用cout,输入用cin,换行用endl,这些都定义在std中。

#include <iostream> using std::cout;
using std::cin;
using std::endl;int main()
{int i = 0;cin >> i;cout << i << endl;return 0;
}

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

那为什么引用头文件之后,还得展开命名空间呢?

下面是比较标准的解释:

在C++中,cout 是 iostream 库   中定义的一个对象,用于输出数据到标准输出(通常是终端或控制台)。iostream 库定义在 std 命名空间中。命名空间是一种将库名字封装起来的方法,以避免不同库中可能存在的名字冲突。

当您在程序中包含 <iostream> 头文件时,您实际上是包含了 iostream 库的所有声明。然而,默认情况下,这些声明都是在 std 命名空间之内的。因此,如果您直接使用 cout 而不指明它属于 std 命名空间,编译器将不知道您所指的 cout 是 std 命名空间中的 cout。

语法糖二:缺省参数

缺省是默认的意思,缺省参数就是指默认的参数,对于C++函数,可以给形参创建一些缺省值,使之成为缺省参数。

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实
参则采用该形参的缺省值,否则使用指定的实参。
举例:
#include <iostream> using std::cout;
using std::cin;
using std::endl;void Date(int year = 2024, int month = 5, int day = 9)
{cout << year << " " << month << " " << day << endl;
}int main()
{Date(2022, 11, 13);Date();return 0;
}

对于Date函数,给定了一些缺省参数。

程序运行结果:

2022 11 13
2024 5 9

当传参时,打印结果是参数结果;不传参时,打印结果是缺省结果。

同时缺省还分为全缺省和半缺省。全缺省是指将所有参数都设置为缺省参数;半缺省是指将部分参数设置为缺省参数,但是半缺省有一定规则:参数必须从右往左缺省,且中间不能跳过参数。

多提一嘴,函数的定义应该声明与定义分离,如果定义在头文件中,那包含头文件时,会同时在符号表存在多个函数符号,出现符号冲突,连接错误。

缺省函数应该在声明时给出缺省参数,而不是定义时,原因如下:

缺省参数通常在函数原型(函数声明)中给出,而不是在函数定义中。函数原型通常位于头文件中,而函数定义位于源文件中。这样做的好处是,你可以确保无论函数在哪里被调用,调用者都能看到缺省参数的值。

总结一些缺省的注意点:

1. 半缺省参数必须从右往左依次来给出,不能间隔着给
void Func(int a, int b = 10, int c = 20)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;
}

2. 缺省参数不能在函数声明和定义中同时出现,一般在声明处给出。
//a.h
void Func ( int a = 10 );
// a.cpp
void Func ( int a = 20 )
{}
// 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该
用那个缺省值
3. 缺省值必须是常量或者全局变量
4. C语言不支持(编译器不支持)

语法糖三:函数重载

函数重载真是C++最甜的语法糖之一了,通过对比C与C++,就可以得知。

讲解函数重载之前,先讲一个笑话,这个笑话就体现了函数重载的思想。

NBA的球员,Lebron曾经在一场比赛中投出10中9的优秀命中率,因此球迷乐呵到“出手十次,一次不进”的笑话。大家可以细细品一品“一次不进”这句话。

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这
些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型
不同的问题。
简而言之,函数重载就是: 函数名相同,参数不同,从而导致功能不同。同时,重载一定是出现在同一作用域。
下面就从Swap函数的实现进行入门讲解。
C语言对于Swap的实现:
void SwapInt(int* pa, int* pb)
{int tmp = *pa;*pa = *pb;*pb = tmp;
}void SwapChar(char* pa, char* pb)
{char tmp = *pa;*pa = *pb;*pb = tmp;
}

在使用时:

int main()
{int a = 10;int b = 2;SwapInt(&a, &b);char m = 'a';char f = 'b';SwapChar(&m, &f);return 0;
}

C++对于swap的实现:


void Swap(int* pa, int* pb)
{int tmp = *pa;*pa = *pb;*pb = tmp;
}void Swap(char* pa, char* pb)
{char tmp = *pa;*pa = *pb;*pb = tmp;
}
int main()
{int a = 10;int b = 2;Swap(&a, &b);cout << a << " " << b << endl;char m = 'a';char f = 'b';Swap(&m, &f);cout << m << " " << f << endl;return 0;
}

可以看到,可以直接使用相同的函数名字,对于不同的参数,函数可以自己去匹配类型。

但是注意函数重载与函数缺省参数同时使用时的冲突问题。

那为什么C不支持函数重载,但是C++支持呢?

那就要从程序的运行阶段去分析了。程序的运行阶段分为:与处理、编译、汇编、链接,四个阶段。

在编译时,会进行符号汇总;汇编会生成符号表;链接会对符号表进行合并与重定位。对于C语言,函数名在生成符号时,往往只是直接利用函数名作为符号,或者只是进行简单的修饰,因此函数名不能冲突;但是C++却有一套“独特的命名规则”,这种命名规则叫做名字修饰(name Mangling)。同时名字修饰也是支持重载的原理。

在Linux下,gcc编译器可以很明显的观察到函数符号的名字修饰规则:

【_Z+函数长度+函数名+类型首字母】

因此构成函数重载时,“类型首字母”的不同,也构成了符号表中符号的不同,不至于在链接阶段出现链接错误。

语法糖四:引用        

先解释下何为引用,引用就是“取别名”。举个例子,国足因为在球场的拉跨表现,被球迷人称为“海参队”,海参队就是球迷们对国足的别称。

引用概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。
在使用引用时,有以下公式:
类型& 引用变量名(对象名) = 引用实体;
int main()
{int a = 10;int& b = a;cout << b << endl;return 0;
}

b就是a的一个引用变量,可以理解为b就是a的“别名”,对b进行操作,就可以控制a

int main()
{int a = 10;int& b = a;++b;cout << a << endl;    //打印11return 0;
}
注意: 引用类型 必须和引用 实体 同种类型 的。
引用特性
1. 引用在 定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体
常引用
观察以下代码,反映了几个情况。
const int a = 10;
int& b = a;
const int a = 10;
    const int& b = a;
int a = 10;
    const int& b = a;
需要注意的是,①存在语法错误,因为出现了“权限扩大”。在引用时,权限:可以缩小、平移,但是不能放大。

int a = 0;
int func()
{a = 3;return a;
}int main()
{int b = func();return 0;
}

这段代码的意思是用b接收返回值,b接收的是数值的拷贝。

int a = 0;
int func()
{a = 3;return a;
}int main()
{const int& b = func();return 0;
}

这段代码表示用b作为引用变量,接收函数的返回值,引用变量b是a的一个别称,可以指向这块空间。但是为什么b需要被const修饰呢?

这是因为函数在返回的时候,会将返回值传递给一个临时的对象,但是引用不能直接绑定到临时对象上(临时对象的生命周期极短),为了防止引用悬空,通常会将引用变量用const修饰,此时这个临时对象的生命周期也会变长。

引用变量的两种应用

引用传参

引用传参是指用引用变量作为形参,接收实参。再拿Swap函数举例。


void Swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}int main()
{int a = 3, b = 5;Swap(a, b);cout << a << " " << b << endl;return 0;
}

既然引用变量只是一个别名,可以指向“本体”,那边可以用引用变量接收形参,进而修改实参。

引用返回

引用返回是利用引用变量接收引用返回。

先看一段传值返回的代码

int Func()
{int n = 3;return n;
}int main()
{int a = Func();return 0;
}

a的值是多少呢?a = 3,其过程是,先将n传给一个临时的对象,a再接收临时对象的值。

传值返回用值接收

再看引用返回的代码

int& Add(int x, int y)
{int z = x + y;return z;
}int main()
{int& a = Add(3, 5);Add(2, 2);cout << a << endl;	// 打印 4 return 0;
}

引用返回是用引用变量接收函数的返回值(返回类型之后需要加一个&,表示引用返回),传引用返回用引用变量接收

为什么a的值是4不是8呢?这就要从函数栈帧的创建于销毁入手。
第一次调用Add函数时
函数在调用完成之后,Add栈帧销毁
第二次调用Add时
可以看到,第二次调用Add时,其栈帧覆盖了第一次调用时的栈帧,而 a只是一个指向“返回空间”
的别称,当第二次调用之后,返回空间中的值被修改,自然a就被修改。
这就有点类似于C语言的野指针问题了。举个例子,当你不住酒店,退房完成之后,此时在道理上你没有访问这间房间的权限,但是你留了一张房卡,没有退回去,这时候就出现了非法访问的问题,即野指针。假如你之前在房间的抽屉里面放了一个苹果,那你再进去的时候,你还能保证房间还有苹果吗?(薛定谔的猫)
同理转移到Add函数上,当你第二次使用Add函数时,由于a作为引用变量,指向的是一块已经销毁的空间,此时再度访问这块空间之后,就不能保证a的值不发生改变。
那正确的引用返回应该是怎样使用的呢?为了防止出现非法的空间,引用返回返回的变量必须是一个出了函数还不会销毁的变量(static修饰、malloc在堆区、全局变量、引用传参的对象)。对于形参用引用传参时,返回的引用参数一直在外部是存在的,可以使用。

C++中int& 函数利用引用返回时,只能使用引用变量接收吗

在C++中,使用引用作为函数返回类型时,通常情况下,返回的是某个变量的别名,而不是一个独立的变量。因此,通常你确实需要使用一个引用变量来接收这个返回值,以便于直接对原始变量进行操作。

因此,在使用引用返回时,一定要注意返回变量会不会出函数留存。

引用传参与引用返回的优点

引用传参(何时都可以使用)

1.提高函数效率

2.输出型参数的修改(如力扣题目returnSize的修改)

引用返回(出函数对象的作用还在,方可使用)

1.提高效率

2.修改返回对象。

看到这里,你或许会疑问,怎么引用与指针如此相似,引用在语法上不会占用空间,其实引用在底层和指针都是一样的。

引用与指针好比是大众与保时捷,在很多时候,往往保时捷的发动机等配置与大众是一样的,只不过在外部的外观不一样。

引用和指针的不同点:

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

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

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

一个同类型实体

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

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

位平台下占4个字节)

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

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

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

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

语法糖五:内联函数

C++们在使用C语言的宏时,发现了宏(宏常量、宏函数)有大量的弊端:

1.没有安全类型的检查

2.不能调试(预处理阶段被替换)

3.可维护性差

因此内联函数便继承了宏的优点:

1.具有复用性

2.不需要栈帧的建立(针对频繁调用的小函数),效率高

同时拒绝了宏的弊端,应运而生。

内联函数的概念

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。
inline是C++的一个新的关键字,用来声明内联函数。
inline int Add(int x, int y)
{int z = x + y;return z;
}int main()
{int a = Add(3, 5);	cout << a << endl;	//打印8return 0;
}

这便是一个内联函数的使用。那内联函数和普通函数有什么区别呢?当然有区别,内联函数不需要建立栈帧,而是在使用的地方直接展开。

可以看到P1不是内联函数,因此有call指令,而call指令就是转到对应函数建立栈帧的标志。

内联函数相比于普通函数的特性

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
用函数体替换函数调用,缺陷:可能会使目标文件变大(代码膨胀)优势:少了调用开销,提高程序运 行效率。
2.inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
下图为 《C++prime》第五版关于inline的建议:
3. 内联函数不可以声明定义分离!因为内联函数会在使用的地方直接展开,在符号表中不会存在有效地址,链接时,无法找到对应有效地址。一般都会定义在头文件中。

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

1. 常量定义 换用const enum

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

语法糖六:auto关键字(C++11)

C++程序在书写一些大型的项目时,类型名常会复杂冗长,因此auto关键字应运而生,这个关键字的能力是根据变量,自动识别变量的类型。
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的
是一直没有人去使用它。C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。由此开始,auto关键词变成了一个香饽饽。
int TestAuto()
{
return 10;
}
int main()
{int a = 10;auto b = a;auto c = 'a';auto d = TestAuto();cout << typeid(b).name() << endl;    //打印变量的类型typeid(参数).name()cout << typeid(c).name() << endl;    cout << typeid(d).name() << endl;//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化return 0;
}
【注意】
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编
译期会将auto替换为变量实际的类型

auto的使用规则

1.对于指针、引用类型的使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须 加&

int main()
{int x = 10;auto a = &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;cout << x << endl;return 0;
}

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

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量
void TestAuto ()
{
auto a = 1 , b = 2 ;
auto c = 3 , d = 4.0 ;   // 该行代码会编译失败,因为 c d 的初始化表达式类型不同
}
3.auto 不能推导的场景
auto不能作为函数形参的类型
// 此处代码编译失败, auto 不能作为形参类型,因为编译器无法对 a 的实际类型进行推导
void TestAuto ( auto a )
{}
auto不能声明数组
void TestAuto ()
{
int a [] = { 1 , 2 , 3 };
auto b [] = { 4 5 6 };
}
为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

语法糖七:基于范围的for循环(C++11)

范围for的语法

在C++98中如果要遍历一个数组,可以按照以下方式进行:
void TestFor ()
{
        int array [] = { 1 , 2 , 3 , 4 , 5 };
        for ( int i = 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 ;
}
对于C++11,范围for的出现, for循环后的括号由冒号“ :”分为两部分:第一部分是范
围内用于迭代的变量,第二部分则表示被迭代的范围(数组名)
简而言之: 迭代变量  :迭代范围
此时再去遍历数组,便可用以下方式。
int main()
{int array[] = { 1, 2, 3, 4, 5 };for (auto& m : array)	//此处用引用变量 m ,去不断指向数组中的元素。m *= 2;for (auto m : array)cout << m << " ";cout << endl;return 0;
}

此处必须用引用变量,才能修改数组的元素,&不能漏掉。这也是auto语法的常用场景。

范围for的强大之处是:1.自动迭代 2.自动判断结束

范围for的使用条件

1. for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供
begin和end的方法,begin和end就是for循环迭代的范围。
注意:以下代码就有问题,因为for的范围不确定
void TestFor ( int array [])
{
for ( auto & e : array )
cout << e << endl ;
}
因为在函数形参中,不存在数组的概念,因此范围不明。
2. 迭代的对象要实现++和==的操作。(后序的知识)

语法八:指针空值nullptr(C++11)

可以看到,这并不是一颗语法糖,而是吃糖吃多了,吃出蛀牙了,此处是填坑。

C++98 中的指针空值
在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现
不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下
方式对其进行初始化:
void TestPtr ()
{
int* p1 = NULL ;
int* p2 = 0 ;
// ……
}
NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif
可以看到,C++而言将NULL定义为数字0,但是C却将NULL定义为指针类型的0,因此在很多地方出现了不良的错误。所以需要填补C++的坑。因此nullptr便出现了。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器
默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void
*)0。
注意:
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为 新关键字 引入
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表 示指针空值时建议最好使用nullptr

以上便是C++发出的语法糖,学完之后,你觉得香不香?

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

自托管站点监控工具 Uptime Kuma 搭建与使用

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 Uptime Kuma 是一个类似 Uptime Robot 的站点监控工具&#xff0c;它可以自托管在自己的 Nas 或者 VPS 上&#xff0c;用来监控各类站点、数据库等 监控类型&#xff1a;支持监控 HTTP(s) / TCP / HTTP(s…

服务丢在tomcat中启动war包,需要在tomcat中配置Java环境吗?

一般来说&#xff0c;部署在 Tomcat 上的 WAR 包启动时不需要在 Tomcat 中单独配置 Java 环境&#xff0c;因为 Tomcat 启动本身就需要依赖 Java 环境。以下是确保 Tomcat 正常运行与部署 WAR 包的基本步骤&#xff1a; 安装 Java 环境&#xff1a; 首先&#xff0c;确保你的系…

springboot增删改查

我的记录 RestController RequestMapping("/user") public class UserController {Autowiredprivate UserService userService;GetMapping("/list")public List<User> list(){return userService.list();}//新增PostMapping("/save")publi…

stm32 FOC系列 直流无刷6步换向控制原理

1、直流无刷电机的简介 直流无刷电机 (Brushless Direct Current Motor&#xff0c;简称 BLDCM) &#xff0c; 其最大的特点就是取消 了传统有刷电机中的电刷和换向器等结构。 因此线圈绕组不参与旋转&#xff0c;而是作为定子&#xff0c;永磁 体作为转子&#xff0c;所以需要…

vue3 antd-vue 超简单方式实现a-table跨页勾选

一、效果如下&#xff1a; 第一页勾选了2&#xff0c; 3&#xff0c; 4 翻到第三页勾选24&#xff0c; 25 回显&#xff0c;如比返回第一页的时候触发分页改变&#xff0c; 在映射中的第一页的数据给到a-table绑定的state.selectedRowKeys即可&#xff0c;如下方法 二、勾选思路…

luceda ipkiss教程 69:导出器件或者线路的三维模型

ipkiss 3.12版加入write_obj函数&#xff0c;可以直接输出器件的三维模型。 如&#xff0c;输出自定义的mmi的三维模型&#xff1a; 代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3class MMI1x2(i3.PCell):"""MMI with …

给网络镜像模式下的 WSL2 使用 127.0.0.1代理的方法

网络镜像模式下的WSL2虽然复制了宿主机windows的ip&#xff0c;但是仍然无法访问127.0.0.1的代理。经过调查&#xff0c;发现因为WSL2从应用商店下载而来&#xff0c;所以可能是UWP应用&#xff0c;所以需要用工具解除环回代理限制。

【吃透Java手写】4-Tomcat-简易版

【吃透Java手写】Tomcat-简易版-源码解析 1 准备工作1.1 引入依赖1.2 创建一个Tomcat的启动类 2 线程池技术回顾2.1 线程池的使用流程2.2 线程池的参数2.2.1 任务队列&#xff08;workQueue&#xff09;2.2.2 线程工厂&#xff08;threadFactory&#xff09;2.2.3 拒绝策略&…

idea运行SpringBoot项目爆红提示出现:Java HotSpot(TM) 64-Bit Server VM warning...让我来看看~

在运行SpringBoot项目的时候&#xff0c;发现总有这个警告提示出现&#xff0c;有点强迫症真的每次运行项目都很难受啊&#xff01;那么今天便来解决这个问题&#xff01; 先来看一下提示内容&#xff1a;Java HotSpot(TM) 64-Bit Server VM warning: Options -Xverify:none an…

智慧公厕:让厕所管理变得更智慧、高效、舒适!

公共厕所是城市的重要组成部分&#xff0c;但常常被忽视。它们的管理和养护往往面临着许多问题&#xff0c;例如卫生状况不佳、环境畏畏缩缩、设施老旧等。为了解决这些问题&#xff0c;智慧公厕应运而生。智慧公厕是一种全方位的应用解决方案&#xff0c;将科技与公共厕所管理…

centos安装mysql-client

直接安装&#xff1a; yum install mysql-community-client报了错误No package mysql-community-client available. 原因&#xff1a;CentOS/RHEL系统默认的软件源中并不包含MySQL软件包&#xff0c;需要通过添加第三方存储库来获取MySQL相关软件 添加源 安装MySQL官方的Yum…

Box86源码解读记录

1. 背景说明 Github地址&#xff1a;https://github.com/ptitSeb/box86 官方推荐的视频教程&#xff1a;Box86/Box64视频教程网盘 2. 程序执行主体图 Box86版本: Box86 with Dynarec v0.3.4 主函数会执行一大堆的初始化工作&#xff0c;包括但不限于&#xff1a;BOX上下文 …

docker端口映射成功,docker端口不生效的问题解决,外界无法访问docker映射端口

docker端口映射不生效的问题解决 问题 使用docker run -p 88848:8848后&#xff0c;显示容器启动正常&#xff0c;并且使用docker logs –f xxx能够看到容器可以正常启用&#xff0c;docker ps 可以看到容器启动成功&#xff0c;并且端口已经映射,但是在浏览器访问相关地址&am…

05-树9 Huffman Codes

05-树9 Huffman Codes &#xff08;30分&#xff09; 题目描述 In 1953, David A. Huffman published his paper “A Method for the Construction of Minimum-Redundancy Codes”, and hence printed his name in the history of computer science. As a professor who gives…

并行执行的4种类别——《OceanBase 并行执行》系列 4

OceanBase 支持多种类型语句的并行执行。在本篇博客中&#xff0c;我们将根据并行执行的不同类别&#xff0c;分别详细阐述&#xff1a;并行查询、并行数据操作语言&#xff08;DML&#xff09;、并行数据定义语言&#xff08;DDL&#xff09;以及并行 LOAD DATA 。 《并行执行…

寻找志同道合的小伙伴,让生活更加多彩

在繁忙的生活中&#xff0c;我们时常渴望找到一个可以倾诉心声、分享喜悦和烦恼的角落。有时候&#xff0c;一个简单的聊天就能让心情变得豁然开朗。而今天&#xff0c;我想向大家介绍一个可以让生活更加多彩的小天地——那是一个充满活力和温暖的QQ群。 群号&#xff1a;78004…

wandb: - 0.000 MB of 0.011 MB uploaded持续出现的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

安卓通信方式简介

目录 一、Binder二、Socket三、Binder与Socket四、Handler 一、Binder Binder作为Android系统提供的一种IPC机制&#xff0c;无论从系统开发还是应用开发&#xff0c;都是Android系统中最重要的组成。 二、Socket Socket通信方式也是C/S架构&#xff0c;比Binder简单很多。在…

如何将图片表格转成excel?分享3种好用的软件!

在信息爆炸的时代&#xff0c;我们每天都会接触到大量的图片表格。这些表格中可能包含着我们需要的各种数据和信息&#xff0c;但是如何将它们快速、准确地转化为Excel格式&#xff0c;以便我们进行编辑、分析呢&#xff1f;今天&#xff0c;就让我们一起来探讨一下如何将图片表…

发票审核如何自查?报销没有发票,如何处理?

在财务管理中&#xff0c;发票是非常重要的一项凭证&#xff0c;是费用核算和税务申报的重要依据&#xff0c;但光靠发票入账可能会被定义为虚开。 一、费用报销审核必看的6个要点 1、票据与实际业务吻合 这是费用报销中最基本的常识&#xff0c;比如&#xff1a;采购一批物料&…