C++入门基础(万字详解)建议收藏!!!

该文章已生成可运行项目,

文章目录

1、什么是C++ ?

2、C++的发展历史

3、C++的版本更新

4、命名空间(namespace)

4.1、命名空间的意义

4.2、命名空间的定义 

4.3、命名空间的使用

5、C++输入&输出

6、缺省参数

6.1、全缺省

6.2、半缺省

7、函数重载

8、引用

8.1、引用的概念及定义

8.2、引用的特性

8.3、引用的使用

8.4、const引用

8.5、引用和指针的关系

9、内联函数(inline)

9.1、定义

9.2、内联函数与宏定义比较

10、空指针常量——nullptr

10.1、传统的NULL

10.2、nullptr的特性

总结


1、什么是C++ ?

C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。   

2、C++的发展历史

       C++的起源可以追溯到1979年,当时BjarneStroustrup(本贾尼·斯特劳斯特卢普)在贝尔实验室从事计算机科学和软件工程的研究工作。面对项目中复杂的软件开 发任务,特别是模拟和操作系统的开发工作,他感受到了现有语言(如C语言)在表达能力、可维护性和可扩展性方面的不足

         1983年,BjarneStroustrup在C语言的基础上添加了面向对象编程的特性,设计出了C++语言的雏形, 此时的C++已经有了类、封装、继承等核心概念,为后来的面向对象编程奠定了基础。这⼀年该语言被正式命名为C++。

        在随后的几年中,C++在学术界和⼯业界的应用逐渐增多。⼀些大学和研究所开始将C++作为教学和研究的首选语言,而⼀些公司也开始在产品开发中尝试使用C++。这⼀时期,C++的标准库和模板等特性也得到了进⼀步的完善和发展。

         C++的标准化工作于1989年开始,并成立了⼀个ANSI和ISO(InternationalStandards Organization)国际标准化组织的联合标准化委员会。1994年标准化委员会提出了第⼀个标准化草案。在该草案中,委员会在保持斯特劳斯特卢普最初定义的所有特征的同时,还增加了部分新特征。 在完成C++标准化的第⼀个草案后不久,STL(StandardTemplateLibrary)是惠普实验室开发的⼀系 列软件的统称。它是由AlexanderStepanov、MengLee和DavidRMusser在惠普实验室工作时所开发出来的。在通过了标准化第⼀个草案之后,联合标准化委员会投票并通过了将STL包含到C++标准中的提议。STL对C++的扩展超出C++的最初定义范围。虽然在标准中增加STL是个很重要的决定,但也因此延缓了C++标准化的进程。

       1997年11月14日,联合标准化委员会通过了该标准的最终草案。1998年,C++的ANSI/IS0标准被投入使用。

3、C++的版本更新

版本时间特点内容
C++981998首个正式 ISO 标准,确立 C++ 基础框架,奠定 C++ 语言规范,首次统一语法和标准库类、继承、多态、模板基础;标准模板库(STL);异常处理;命名空间
C++032003对 C++98 的小幅修订,以修复漏洞为主,提升标准严谨性,保持与 C++98 的兼容性修正标准中的模糊表述和不一致性(如模板实例化规则);无新增重大特性
C++112011颠覆性更新,引入现代编程特性,被称为 “新 C++” 起点,大幅提升开发效率,减少内存管理错误,让 C++ 适应现代软件开发需求自动类型推导(auto)、lambda 表达式、decltype(获取表达式类型)、范围 for 循环;智能指针(shared_ptr/ unique_ptr)、移动语义(std::move)、右值引用(&&),减少拷贝开销;nullptr(替代NULL,避免歧义)、constexpr(编译期常量计算)、委托构造函数、继承构造函数等。
C++142014完善 C++11,聚焦易用性提升泛型 lambda;函数返回类型自动推导;变量模板;std::make_unique
C++172017增强代码简洁性和性能,引入更多 “零成本抽象”结构化绑定;if constexpr;并行算法库;折叠表达式;std::optional/std::variant/std::any
C++202020继 C++11 后又一次重大更新,引入底层架构改进

解决头文件依赖问题,简化模板错误信息,支持高效异步编程,扩展算法表达能力。

模块(Modules);概念(Concepts);协程(Coroutines);范围库(Ranges);三路比较运算符(<=>)

C++232023优化 C++20 新特性,提升实用性完善模块系统;标准化 std::generator;新增 std::flat_map;简化 if/switch 初始化器语法
C++262026制定中

4、命名空间(namespace)

4.1、命名空间的意义

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

C语言项目类似下面程序这样的命名冲突是普遍存在的问题,C++引⼊namespace就是为了更好的解决这样的问题。

当我们在#include<stdlib.h>头文件下定义一个全局变量rand;

#include<stdlib.h>
int rand = 1;//定义一个全局变量

int main()
{
	printf("%d\n", rand);
	return 0;
}

我们发现这时候编译报错,就是因为我们定义的变量名与标准库里面的函数名都在同一个域当中,因此发生冲突了,所以,我们用命名空间来额外定义一个与全局域相独立的域,这样就不会发生命名冲突了。

4.2、命名空间的定义 

1、定义namespace

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。当我们想要访问命名空间中的成员,就需要用到命名空间的名字 + " :: " 操作符 + 成员名(::为域作用限定符)

#include<stdlib.h>
//定义一个命名空间
namespace hds
{
	int rand = 1;//命名空间的成员
}

int main()
{
	printf("%p\n", rand);//打印函数的地址
	printf("%d\n", hds::rand);//访问命名空间hds的成员
	return 0;
}

 输出结果:  namespace本质是定义出⼀个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。   

 2、一个namespace中同时定义变量/函数/类型

命名空间可以定义变量、函数以及类型。但是需要注意的是在定义结构体类型时,在访问时是要在 struct 后面写的。同时同一个工程中是允许出现同名的命名空间的,在编译时编译器会自动将其合并。

namespace hds
{
    //定义变量
	int rand = 10;
    //定义函数
	int Add(int x, int y)
	{
		return x + y;
	}
    定义结构体类型
	struct Node
	{
	    struct Node* next;
		int val;
	};
}

3、嵌套定义

//命名空间的嵌套
namespace hds
{
	int rand = 5;

	namespace zm
	{
		int rand = 10;
	}

	//...
}

4.3、命名空间的使用

编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以我们要访问或者使用命名空间中定义的变量和函数,有三种方式:

• 指定命名空间访问,项目中推荐这种方式。

using + 命名空间的名字 + 成员名:将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。就比如在下面的命名空间中,我们在程序中会经常使用Swap函数,我们就可以将命名空间中的Swap函数单独展开出来使用,避免每次都用命名空间的名字 + " :: " 操作符 + 成员名这样的方式取使用,也比较麻烦。

using namespac 命名空间的名字展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用

namespace hds
{
	int x = 5;
	int y = 10;

	void Swap(int* a, int* b)
	{
		int tmp = *a;
		*a = *b;
		*b = tmp;
	}
}

using hds::Swap;  //将命名空间中的Swap函数展开

//using namespace hds;  //将命名空间中所有成员展开

5、C++输入&输出

<iostream>是C++标准的输入、输出流库,定义了标准的输入、输出对象。所以,当我们想要用C++输入输出时,需要包含头文件#include<iostream>

std :: cin 是istream类的对象,它主要面向窄字符的标准输入流 std::cout 是ostream类的对象,它主要面向窄字符的标准输出流 std::endl 是⼀个函数,流插入输出时,相当于插入⼀个换行字符加刷新缓冲区cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去使用他们。在日常练习程序过程中,为了方便,我们可以用using namespace std 的方式将std这个命名空间全部展开,这样我们在使用cout/cin/endl时就不用加std::了。

• << 是流插入运算符,>> 是流提取运算符 (C语言还用这两个运算符做位运算左移/右移)

#include<iostream> //是标准的输⼊、输出流库
using namespace std;  //将命名空间中所有成员展开(不推荐)

int main()
{
	int a = 5;
	double b = 2.5;
	char c = 'x';
   
    //输出
	cout << a << " " << b << " " << c << endl;
	std::cout << a << " " << b << " " << c << endl;

    //输入
    // 可以⾃动识别变量的类型
    cin >> a >> b >> c;
	cout << a << " " << b << " " << c << endl;

	return 0;
}

6、缺省参数

缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调用该函数时,如果没有指定实参 则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。(有些地方把 缺省参数也叫默认参数),带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。

void Fun(int a = 1)
{
	cout << a << endl;
 }

对于Fun函数,给形参a一个缺省值1,形参a即为缺省参数。

6.1、全缺省

全缺省就是全部形参给缺省值。

//全缺省
void Func(int a = 1, int b = 2,int c = 3)
{
	cout << a << " " << b << " " << c << endl;
}

对于Func函数,给所有的形参都给一个缺省值。

6.2、半缺省

半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。

//半缺省:C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
void Fund(int a, int b = 8, int c = 9)
{
	cout << a << " " << b << " " << c << endl;
}

对于Fund函数的形参,在版缺省的情况下,必须将缺省值从右往左连续给形参。

注意:函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。

7、函数重载

C++支持在同⼀作用域中出现同名函数,但是要求这些同名函数的形参不同,而形参的不同分为参数类型不同参数个数不同以及参数类型的顺序不同。系统会根据它们的参数类型自动匹配最相符的函数进行调用,即使函数名相同也是可以的。这样C++函数调用就表现出了多态行为,使用更灵活。C语言是不支持同⼀作用域中出现同名函数的。

那么编译器是如何区分的呢?

其实在编辑器内部调用这个函数时,是通过它的函数名加地址去寻找它的,每一个函数在链接时,编译器都会在你书写的函数名(Fun)的基础上再根据它的参数来进行添加一些符号来修饰它。这样就可以区分同名函数了。

1、参数类型不同,函数名均为Add,让我们来看当我们传不同类型的参数时,是否会调用相应的函数。

//参数类型不同
//两个int类型的数相加
int Add(int x,int y)
{
	cout << "Add(int x,int y)" << endl;
	return x + y;
}

//两个double类型的数相加
double Add(double x, double y)
{
	cout << "Add(double x, double y)" << endl;
	return x + y;
}

我们看到,当我们将两个整型数据作为实参调用Add函数时,调用了Add(int x, int y)函数;当我们将两个double类型的数据作为实参调用Add函数时,调用了Add(double x, double y)函数。

2、参数个数不同

//参数个数不同
void F()
{
	cout << "F()" << endl;
}

void F(int a)
{
	cout << "F(int a)" << endl;
}

我们看到,当我们在没有实参的情况下调用F函数时,调用了F()函数;当我们在只有一个实参的情况下调用F函数时,调用了F(int a)函数。

3、参数类型顺序不同

//参数类型顺序不同
void Fun(int a, char b)
{
	cout << "Fun(int a, char b)" << endl;
}

void Fun(char a, int b)
{
	cout << "Fun(char a, int b)" << endl;
}
// 但是f1()调⽤时,会报错,存在歧义,编译器不知道调用谁
void f1()
{
     cout << "f()" << endl;
}
void f1(int a = 10)//缺省参数
{
	cout << "f(int a)" << endl;
}

注意:

1、返回值不同不能作为重载条件。因为如果两个函数函数名和参数是一样的,调用时编译器没办法区分,返回值不同是不构成重载的。

2、下面两个函数构成重载,但是f1()调用时,会报错,存在歧义,编译器不知道调用谁。

void f1()
{
     cout << "f()" << endl;
}
void f1(int a = 10)//缺省参数
{
    cout << "f(int a)" << endl;
}

8、引用

8.1、引用的概念及定义

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

类型& 引⽤别名=引⽤对象;

C++中为了避免引入太多的运算符,会复用C语言的一些符号,比如前面的<<和>>,这里引用也和取地址使用了同一个符号&,大家注意使用方法角度区分就可以。

int main()
{
	int a = 5;
	int& b = a; //给引用对象a取引用别名为b
	int& c = b; //给引用对象b取引用别名为c
    
    //观察a,b,c的地址
	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;

	cout << a << " " << b << " " << c <<endl;
	
    a++;
	cout << a << " " << b << " " << c <<endl;
	return 0;
}

我们看到,a,b,c 的地址相同,且a,b,c 的值相同,当a执行了后置++操作后,a,b,c的值同时增加且相同。所以,b,c和它引用的变量a共用同⼀块内存空间。

8.2、引用的特性

1、引用在定义时必须初始化,否则会报错,就像我们取外号必须找一个对象。

2、一个变量可以有多个引用,类似于一个人可以有多个绰号,但是都指的是这一个人。

int main()
{
	int a = 5;
	int& ra = a;
	int& pa = a;
	return 0;
}

3、引用一旦引用一个实体,再不能引用其他实体。类似于一个外号就只能给一个人用,如果给很多人取同一个外号,那岂不是乱套了。

int main()
{
	int a = 5;
	int& ra = a;

	int c = 10;
	// 这⾥并非让ra引⽤c,因为C++引⽤不能改变指向
	// 这⾥是⼀个赋值
	ra = c;
	cout << a << " " << ra << " " << c << endl;
	return 0;
}

8.3、引用的使用

引用在实践中主要是于引用传参引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被 引用对象

1、 作为函数参数(传递引用)

  • 解决函数传参时的 “值拷贝” 问题,尤其适合大型对象(如结构体、类),提高效率。
  • 允许函数修改外部变量的值(类似指针的效果,但语法更简洁)。
void Swap(int& x, int& y) //x和y是实参的引用
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 2, b = 5;
	cout << a << " " << b << endl;

	Swap(a, b);
	cout << a << " " << b << endl;
	return 0;
}

2、作为函数返回值(返回引用)

  • 避免函数返回时的临时对象拷贝,提高效率。
int& getMax(int& a, int& b) 
{
    return (a > b) ? a : b;  // 返回较大值的引用
}

int main() 
{
    int x = 10, y = 20;
    getMax(x, y) = 30;      // 直接修改返回的引用(等价于 y = 30)
    cout << y;              // 输出 30
    return 0;
}

注意不能返回函数内部局部变量的引用(局部变量在函数结束后销毁,引用会变成 “悬空引用”,访问时会导致未定义行为)。

// 错误示例:返回局部变量的引用
int& getLocalRef() 
{
    int local = 100;  // 局部变量,函数结束后会被销毁
    return local;     // 返回局部变量的引用(危险!)
}

int main() 
{
    int& ref = getLocalRef();  // ref 成为悬空引用

    // 第一次访问可能看似"正常"(实际是访问已释放的内存)
    cout << "第一次访问: " << ref << endl;

    // 执行其他操作可能覆盖原内存区域
    int x = 200;
    cout << "定义新变量后再次访问: " << ref << endl;

    return 0;
}
// 正确示例:返回值 或 引用全局变量
int getLocalValue() 
{
    int local = 100;
    return local;  // 返回值(安全)
}

int global = 200;//定义全局变量
int& getGlobalRef() 
{
    //static global = 100;  //static修饰,静态变量不销毁,可作引用返回
    return global;  // 返回全局变量的引用(安全,全局变量生命周期与程序一致)或者静态变量的引用
}

8.4、const引用

在C++ 中,const 引用(常量引用)是指被 const 修饰的引用,声明形式为: const +类型+&+引用名。它的核心特点是不能通过引用修改被引用的变量,同时具有一些特殊的使用场景和优势。。const引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大。

1、只读性:通过 const 引用无法修改被引用的变量,保证了数据的安全性。

int a = 10;
const int &ref_a = a;  // const 引用绑定到 a,此时即权限缩小

// ref_a = 20;  // 错误!const 引用不允许修改被引用的变量
a = 20;  // 合法:可以直接修改原变量,ref_a 会随之变化
cout << ref_a;  // 输出 20(反映原变量的最新值)

2、需要注意的是类似 int& ra = a * 3int& rb = a + bdouble d = 12.34; int& rd = d; 这样⼀些场景下a*3的结果以及a+b的结果保存在⼀个临时对象中 int& rd = d 也是类似,在类型转换中会产生临时对象存储中间值,也就是,ra,rb和rd引用的都是临时对象,而C++规定临时对象具有常性,所以这里就触发了权限放大,必须要用常引用才可以。

// 绑定字面量(临时对象)
const int& ref1 = 100;  // 合法:const 引用可绑定临时值

// 绑定表达式结果(临时对象)
int a = 10, b = 20;
const int& ref2 = a + b;  // 合法:表达式结果是临时值

// 绑定表达式结果(临时对象)
const int& ref3 = 3 * a;   // 合法:表达式结果是临时值

// 绑定不同类型(隐式转换后)
double d = 3.14;
const int& ref4 = d;  // 合法:d 被隐式转换为 int 临时值,再绑定到 ref4

8.5、引用和指针的关系

C++中指针和引用就像两个性格迥异的亲兄弟,指针是哥哥,引用是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有自己的特点,互相不可替代。

• 语法概念上引用是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。

• 引同在定义时必须初始化,指针建议初始化,但是语法上不是必须的。

• 引用在初始化时引用⼀个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。 • 引用可以直接访问指向对象,指针需要解引用才是访问指向对象。

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

• 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全一些。

特性引用(Reference)指针(Pointer)
初始化要求引用要求必须初始化指针建议初始化,但是语法上没有要求
空值合法性不允许为空(必须绑定有效变量)可以为 nullptr(可能导致空指针错误)
语法操作直接使用变量名(无需解引用)需要 * 解引用访问目标,& 取地址
const 修饰const int& 表示引用的目标不可修改int *const p 表示指针本身不可修改;const int *p 表示目标不可修改
作为函数返回值不能返回局部变量的引用(会导致悬空引用)可以返回局部变量的地址(但同样会导致悬空指针)
类型转换仅允许隐式转换后绑定到 const 引用(如 double → const int &需强制转换(如 (int*)&d),风险更高

面试题:关于引用以下说法错误的是( )。(阿里巴巴2015笔试题)

A.引用必须初始化,指针不必

B.引用初始化以后不能被改变,指针可以改变所指的对象

C.不存在指向空值的引用,但是存在指向空值的指针

D.一个引用可以看作是某个变量的一个“别名”

E.引用传值,指针传地址

F.函数参数可以声明为引用或指针类型

答案:E。

A.引用必须初始化,必须在定义引用时明确引用的是哪个变量或者对象,否则语法错误,指针不初  始化时值为随机指向

B.引用一旦定义时初始化指定,就不能再修改,指针可以改变指向

C.引用必须出示化,不能出现空引用,指针可以赋值为空

D.简单粗暴的引用理解可以理解为被引用变量或对象的"别名"

E.引用表面好像是传值,其本质也是传地址,只是这个工作有编译器来做,所以错误

F.函数调用为了提高效率,常使用引用或指针作为函数参数传递变量或对象

9、内联函数(inline)

9.1、定义

用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,就可以提高效率。

inline 是对编译器的建议,而非强制命令。编译器会根据函数复杂度(如是否包含循环、递归等)决定是否真正内联。

inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地 址,链接时会出现报错。

// 声明内联函数
inline int Add(int a, int b) 
{
    return a + b;
}

int main() 
{
    int result = Add(3, 5);  // 编译器可能直接替换为 3 + 5
    return 0;
}

内联函数的作用就是将函数在调用的地方直接展开,也就是不去调用函数了。这时候其实我们会发现这和C语言中的宏比较相似。

#define Add(a,b) ((a) + (b))

这是用宏实现了一个加法函数,我们知道宏就是直接在相应的位置替换,但是我们知道操作符是有优先级的,直接替换过去是有可能发生操作符优先级不正确导致出错的。例如;

#define MULTIPLY(a, b) a * b

int main() 
{
    int result = MULTIPLY(2 + 3, 4);  // 实际被替换为 2 + 3 * 4 = 14(而非预期的 5*4=20)
    return 0;
}

而内联函数能避免这类问题:遵循函数语法,逻辑清晰

inline int multiply(int a, int b) 
{
    return a * b;
}

int main() 
{
    int result = multiply(2 + 3, 4);  // 正确计算 5*4=20
    return 0;
}

9.2、内联函数与宏定义比较

特性内联函数宏定义
类型安全有类型检查,参数类型不匹配会报错无类型检查,可能因参数类型错误导致隐藏问题
调试支持可在函数体内设置断点调试宏在预处理阶段被替换,无法直接调试
语法复杂度遵循函数语法,逻辑清晰需注意括号使用(如 (a) + (b)),否则易出错
适用场景简单函数,需类型安全和调试支持简单文本替换,兼容性场景

10、空指针常量——nullptr

10.1、传统的NULL

C语言中我们给一个指向未知的指针常常做这样的处理

int main()
{
	int a = 5;
	int* pa = NULL;

	return 0;
}

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

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

在 C++ 中,NULL 通常被定义为宏:#define NULL 0 即本质上是一个整数 0。这会导致一些类型歧义问题

void func(int x) {
    std::cout << "调用 int 版本" << std::endl;
}

void func(char* p) {
    std::cout << "调用指针版本" << std::endl;
}

int main() {
    func(NULL);  // 歧义:调用的是 func(int) 还是 func(char*)?
    return 0;
}

上述代码中,func实际会调用 func(int)(因为 NULL 被解析为 0),这与开发者 “传递空指针” 的意图不符。而 nullptr 的出现正是为了明确表示 “空指针”,避免这种歧义。

10.2、nullptr的特性

1独立的类型

C++11中引入nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字面量,它可以转换 成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被 隐式地转换为指针类型,而不能被转换为整数类型

int* p1 = nullptr;       // 合法:转换为 int*
char* p2 = nullptr;      // 合法:转换为 char*
// int x = nullptr;      // 错误:不能转换为整数
nullptr_t np = nullptr;  // 合法:显式使用 nullptr_t 类型

2解决函数重载歧义

使用 nullptr 作为实参时,会优先匹配指针类型的重载函数:

func(nullptr);  // 明确调用 func(char*),符合“传递空指针”的意图

3与 NULL 的兼容性

对于旧代码中使用 NULL 的场景,nullptr 可以直接替代,且更安全:

int* p = NULL;   // 旧写法,本质是 p = 0
int* q = nullptr; // 新写法,明确是空指针

总结

C++入门的基础知识还是比较多的,细节也比较多,确实需要我们多多的总结和记忆。如果我的这篇文章中大家发现有什么错误,希望大家多多包涵,同时,欢迎各位大佬的指正。本次的分享就到此结束,我们下期再会。最后,如果大家觉得我的这篇文章还可以的话,可以点个赞支持一下呦!!!

本文章已经生成可运行项目
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值