C++:入门学习C++,它在C的基础上做了哪些修改?

2023-11-19

命名空间

首先看这样的代码:

#include <stdlib.h>
#include <stdio.h>

int rand = 0;

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

上述代码可以编译通过吗?很明显是不可以的,原因在于头文件stdlib中包含了rand函数,因此在定义变量时就不可以再继续使用rand作为你的变量了

而在未来写工程项目中,这样的情况会遇见很多,在包含某个头文件后,代码中的许多变量就不可以再使用了,这就体现出了C语言的一部分局限性

那么C++在C语言的基础上很好的改善了这个问题,C++引入了命名空间的概念,把变量命名在某个空间内,这样就能很好的解决这个问题

命名空间的定义很自由,可以定义变量,定义函数,定义结构体,甚至可以嵌套定义

namespace zbh
{
	//定义变量
	int test = 0;

	//定义函数
	int Add(int x, int y)
	{
		return x + y;
	}

	//定义结构体
	struct MyStruct
	{
		int a;
		int b;
	};

	//命名空间可以嵌套
	namespace free
	{
		int print1()
		{
			return 1;
		}
	}
}

命名空间是如何使用的?C++如何保证命名空间的独立性?

  1. 使用变量时单独说明
  2. 前面定义使用命名空间中的某个函数或变量等
  3. 直接展开
#include <stdio.h>

namespace zbh
{
	//定义变量
	int test = 0;

	//定义函数
	int Add(int x, int y)
	{
		return x + y;
	}

	//定义结构体
	struct MyStruct
	{
		int a;
		int b;
	};

	//命名空间可以嵌套
	namespace free
	{
		int print1()
		{
			return 1;
		}
	}
}

using zbh::Add;

int main()
{
	printf("%d\n", zbh::test);
	printf("%d\n", Add(1,2));
	printf("%d", zbh::free::print1());
}

上面对命名空间的定义也可以省略,直接在main函数前加上

using namespace zbh;

即可直接在后面的函数中使用,通过这样的方式即实现了函数命名空间的独立化

缺省参数

C++中对于函数参数定义了缺省参数,可以理解为,如果我对函数参数中的成员赋给了它一个初值,那么在后续调用的过程中,如果我并未给函数传参,那么函数就会使用默认的参数

具体样例如下所示

#include <iostream>
using namespace std;

void f(int a = 10, int b = 20, int c = 30)
{
	cout << a << " " << b << " " << c << endl;
}

int main()
{
	f();
	f(1);
	f(1, 2);
	f(1, 2, 3);
	return 0;
}

运行结果如下:
在这里插入图片描述
在实际中这样的操作有什么作用??

在定义顺序表中,我们使用的是动态开辟的顺序表,那么在初始化阶段我们是不是可以利用缺省参数优化一些步骤?

首先看C语言实现过程中的方法

#include <stdio.h>
#include <stdlib.h>

typedef int SLDataType;
typedef struct Seqlist
{
	SLDataType* a;
	int size;
	int capacity;
};

void SeqlistInit(Seqlist* s)
{
	s->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);
	s->size = 0;
	s->capacity = 4;
}

这样的实现实际上把容量写死了,不管要开辟多大的顺序表我们都是先开辟容量为4的顺序表再后续进行扩容,而扩容用的realloc是有消耗的

但假设如果我们使用缺省参数进行实现这个函数,可以优化很多

#include <iostream>
using namespace std;

typedef int SLDataType;
typedef struct Seqlist
{
	SLDataType* a;
	int size;
	int capacity;
}Seqlist;

void SeqlistInit(Seqlist* s,int capacity=4)
{
	s->a = (SLDataType*)malloc(sizeof(SLDataType) * capacity);
	s->size = 0;
	s->capacity = capacity;
}

int main()
{
	Seqlist sq,sl;
	SeqlistInit(&sl);
	SeqlistInit(&sq, 100);
	return 0;
}

调用监视观察可以看到

在这里插入图片描述
利用缺省参数,我们确实实实在在实现了自由确定自己想要的容量

函数重载

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

下面展示了什么是函数重载

#include <iostream>
using namespace std;

void func(int a, double b)
{
	cout << "void func(int a, double b)" << endl;
}

void func(double b, int a)
{
	cout << "void func(double b, int a)" << endl;
}

int main()
{
	func(1, 1.1);
	func(2.2, 2);
	return 0;
}

当同名函数中参数个数或参数类型不同时,构成了函数重载,编译器会自动识别传参的个数和类型对应不同的函数重载类型

那为什么C++支持,但是C语言不支持呢?

这里需要讲到一个程序运行起来需要经历的过程

预处理,编译,汇编,链接

假设现在定义了一个Add函数,这个Add函数是在a.cpp文件中定义的,而我要在b.cpp中使用这个函数,当使用后运行程序时,编译器要在b.cpp中使用该函数,但是却找不到这个函数的地址,因此编译器会去b.cpp的符号表中去寻找
Add的地址,然后把这两个链接在一起

在链接的过程中,C和C++就产生了不一样的地方,每个编译器都有自己专属的函数名修饰的规则

下面展示的是C语言编译器的展示结果:
在这里插入图片描述
从中可以看出,在编译完成后,函数名字的修饰依旧是函数的名字,没有实质性的变化

下面再看C++编译器中的样例

在这里插入图片描述
对比C语言的编译器可以很清楚的看到,C++的编译器在编译的过程中把函数名进行了一定程度的修饰,在Linux的编译器下是把函数的参数类型和参数个数也加到了函数名字当中

因此,从中就知道了,为什么C语言不支持函数重载,而C++支持了,就是因为C语言编译器对函数名就是函数名,而C++的编译器对函数名做了参数的引入进行修饰,因此重载后的函数有不同的函数名

从中也就能很轻松的理解,为什么函数参数的类型和个数一样,只有返回值不一样的时候,无法构成函数重载,就是因为编译器对于两个同名的函数不知道该调用哪一个

引用

C++在C的基础上做出的另一大调整就是引用

简单来说,引用就是给变量起了一个别名,变量本身和引用一起控制变量所在的区域

有下面的代码:

void test2()
{
	int a = 10;
	int& b = a;
	cout << &a << endl;
	cout << &b << endl;
}

那么这个程序的运行结果是多少呢

在这里插入图片描述
从中可以看出,引用并不是单独再开辟一块空间用来管理所指向的对象,而是直接和原来的变量一起控制某块区域内的内容

引用的一些特性

  1. 引用在定义的时候必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦确认了一个实体,就不能引用其他内容了

引用作为函数返回值

引用作为函数返回值是一件危险的事,但如果使用正确是有高回报的

我们以下面的操作为例

int& Count()
{
	int n = 1;
	n++;
	return n;
}

void test2()
{
	int a = 10;
}

int main()
{
	int& ret = Count();
	cout << ret << endl;
	test2();
	cout << ret << endl;
	return 0;
}

输出结果为2和10,这是为什么?

原因上升到了函数栈帧的问题,首先画出函数栈帧

下面画出的是main函数执行前两行时候的操作,后续还未画出

在这里插入图片描述
这里定义了&ret,它接收了来自Count函数中的返回值int& n,实际上,这里的ret已经具有了管理n那块区域的能力,而我们又知道,函数在结束后栈帧会被销毁,这里的销毁只是失去了对内存中这片区域的管理权,内存中这块区域本身还是存在的

因此,当下面执行test2函数时,又会开辟一块新的栈帧,而这块栈帧所在的位置和Count函数的栈帧是有很大重合区域的

在这里插入图片描述
因此,这里的ret已经具备了管理不属于它的空间的能力,它可以随时访问一块已经不属于它的区域,因此这里原本是n的空间,现在已经变成了a,但是它依旧可以访问,因此访问出的结果就是10了

因此,这里有需要注意的地方:

==如果函数返回时,出了函数的作用域,如果返回对象还在(没有还给系统),那么这里就可以使用引用返回,如果是像这样的临时变量或者局部变量,就必须使用传值返回

那么说回来,既然引用作为返回值或者参数如此危险,那它有什么使用的必要?

引用的收益

以函数传参为例,如果不用传引用会在函数传参的时候构建一个形参,如果传递的是对象,那么会执行很多默认的成员函数,这也是性能上的损耗,而如果传递的是引用,直接把已经创建好的对象引用到这里,略去了形参创建再销毁这个过程

传引用传参的优势

  1. 提高效率
  2. 输出型参数(形参的改变可以影响实参,类似于指针,但比指针简单一些)

继续谈作为引用返回的优势,作为引用返回也是诸多好处,当然是使用正确的前提;有这样的原则,什么时候可以使用传引用返回?结论是当返回的内容出了函数作用域不被销毁就可以,这是有相当高收益的,在类内的很多成员函数中,返回的通常是*this,而this并不会被销毁,这时用传引用的返回值就很值得

传引用返回的优势

  1. 提高效率
  2. 可以修改返回对象

综上所述, 传值传参还是传值返回值都会创建对象,而使用引用就避免了无效的,多余的创建带来的性能损耗

引用和指针的区别?

语法概念来讲引用是别名,没有自己的空间,它存在的本身就是和实体公用一块空间

但是
底层实现上引用是有自己的空间的,引用本身就是用指针的方式实现的

引用和指针的对比

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
    位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

内联函数

首先,什么是内联函数?

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。

我们都知道,当main函数执行到某个函数时,会在内存中建立该函数的栈帧,再call(调用),而建立函数栈帧是有成本的,如果频繁的建立一些可能只有很少语句的函数会浪费程序运行的效率,因此内联函数的产生就解决了这个问题

内联函数工作的原理,就是在编译阶段,直接把函数体替换为函数调用,从汇编中可以看出,正常函数是有函数调用和函数栈帧的创建的,而内联函数直接把函数体全部都放到汇编

现在我们有这样的代码:

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

int main()
{
	int a = 1;
	int b = 2;
	int c = add(a, b);
}

通过汇编来观察

在这里插入图片描述
当执行到add函数时,确确实实是调用了add函数,那么假如把add函数设为内联函数

inline int add(int a, int b)
{
	return a + b;
}

int main()
{
	int a = 1;
	int b = 2;
	int c = add(a, b);
}

再来看它的汇编代码,就会发现不再是函数调用,而是直接把函数体展开

在这里插入图片描述

内联函数的特性

这么来看,内联函数确实是很有用,但内联函数有它不可忽视的弊端

内联函数是一种用空间换时间的方法,不可否认,在这里略去了函数建立栈帧所需要的时间消耗,但是在编译阶段,内联函数会把函数调用全部替换为函数体,这会使得目标文件变大

因此,内联函数只是一个建议,只是建议编译器可以把这个函数当成内联函数来处理,但是具体到底有没有把它当成内联函数来处理还要看编译器本身,一般来说,会把函数规模比较小,不是递归函数,调用很频繁的函数设置为内联函数

同时,内联函数不应该声明和定义分离,内联函数被展开后,函数地址也就不复存在了,链接过程中就无法找到链接

内联函数和宏的关系

在C语言中,引入了宏,宏看似是一个很好的功能,但其中也有很多弊端

宏的优缺点?

优点:

  1. 增强代码的复用性。
  2. 提高性能。

缺点:

  1. 不方便调试宏。(因为预编译阶段进行了替换)
  2. 导致代码可读性差,可维护性差,容易误用。
  3. 没有类型安全的检查 。

因此,内联函数的出现也算是弥补了宏定义函数带来的诸多不便

指针空值问题

在良好的编程习惯中,定义一个变量要给它一定的初始值,因此在C语言中,我们定义一个指针时,对它的初始化常常是NULL

NULL实际上是一个宏

#define NULL  0

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

void f(int)
{
	cout << "f(int)" << endl;
}
void f(int*)
{
	cout << "f(int*)" << endl;
}
int main()
{
	f(0);
	f(NULL);
	f((int*)NULL);
	return 0;
}

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。

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

因此,在C++的新标准中就引入了关于空指针:

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++:入门学习C++,它在C的基础上做了哪些修改? 的相关文章

  • 如何在MVVM中管理多个窗口

    我知道有几个与此类似的问题 但我还没有找到明确的答案 我正在尝试深入研究 MVVM 并尽可能保持纯粹 但不确定如何在坚持模式的同时启动 关闭窗口 我最初的想法是向 ViewModel 发送数据绑定命令 触发代码来启动一个新视图 然后通过 X
  • 如何检查图像对象与资源中的图像对象是否相同?

    所以我试图创建一个简单的程序 只需在单击图片框中更改图片即可 我目前只使用两张图片 所以我的图片框单击事件函数的代码 看起来像这样 private void pictureBox1 Click object sender EventArgs
  • 如何使 Windows 窗体的关闭按钮不关闭窗体但使其不可见?

    该表单有一个 NotifyIcon 对象 当用户单击 关闭 按钮时 我希望表单不关闭而是变得不可见 然后 如果用户想再次查看该表单 可以双击系统托盘中的图标 如果用户想关闭表单 可以右键单击该图标并选择 关闭 有人可以告诉我如何使关闭按钮不
  • 如何验证文件名称在 Windows 中是否有效?

    是否有一个 Windows API 函数可以将字符串值传递给该函数 该函数将返回一个指示文件名是否有效的值 我需要验证文件名是否有效 并且我正在寻找一种简单的方法来完成此操作 而无需重新发明轮子 我正在直接使用 C 但针对的是 Win32
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 从父类调用子类方法

    a doStuff 方法是否可以在不编辑 A 类的情况下打印 B did stuff 如果是这样 我该怎么做 class Program static void Main string args A a new A B b new B a
  • WPF 中的调度程序和异步等待

    我正在尝试学习 WPF C 中的异步编程 但我陷入了异步编程和使用调度程序的困境 它们是不同的还是在相同的场景中使用 我愿意简短地回答这个问题 以免含糊不清 因为我知道我混淆了 WPF 中的概念和函数 但还不足以在功能上正确使用它 我在这里
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • C# 中的递归自定义配置

    我正在尝试创建一个遵循以下递归结构的自定义配置部分
  • 如何衡量两个字符串之间的相似度? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 给定两个字符串text1 and text2 public SOMEUSABLERETURNTYPE Compare string t
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的
  • 在 Dynamics CRM 插件中访问电子邮件发件人地址

    我正在编写一个 Dynamics CRM 2011 插件 该插件挂钩到电子邮件实体的更新后事件 阶段 40 pipeline http msdn microsoft com en us library gg327941 aspx 并且在此阶
  • WCF:将随机数添加到 UsernameToken

    我正在尝试连接到用 Java 编写的 Web 服务 但有些东西我无法弄清楚 使用 WCF 和 customBinding 几乎一切似乎都很好 除了 SOAP 消息的一部分 因为它缺少 Nonce 和 Created 部分节点 显然我错过了一
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • x86 上未对齐的指针

    有人可以提供一个示例 将指针从一种类型转换为另一种类型由于未对齐而失败吗 在评论中这个答案 https stackoverflow com questions 544928 reading integer size bytes from a
  • 如何使用 std::string 将所有出现的一个字符替换为两个字符?

    有没有一种简单的方法来替换所有出现的 in a std string with 转义 a 中的所有斜杠std string 完成此操作的最简单方法可能是boost字符串算法库 http www boost org doc libs 1 46
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • 使用按位运算符相乘

    我想知道如何使用按位运算符将一系列二进制位相乘 但是 我有兴趣这样做来查找二进制值的十进制小数值 这是我正在尝试做的一个例子 假设 1010010 我想使用每个单独的位 以便将其计算为 1 2 1 0 2 2 1 2 3 0 2 4 虽然我
  • 恢复上传文件控制

    我确实阅读了以下帖子 C 暂停 恢复上传 https stackoverflow com questions 1048330 pause resume upload in c 使用 HTTP 恢复上传 https stackoverflow

随机推荐

  • MYSQL中索引与主键的区别

    MYSQL中索引与主键的区别 索引 索引好比是一本书的目录 可以快速的通过页码找到你需要的那一页 惟一地标识一行 主键 做为数据库表唯一行标识 作为一个可以被外键有效引用的对象 索引是一种特殊的文件 InnoDB数据表上的索引是表空间的一个
  • Unity中的重载和重写

    Unity中的重载和重写 一 重载 二 重写 三 重载和重写的区别 一 重载 重载 两个必须一个可以 参数名必须相同 参数列表必须不同 返回值类型可以不同 代码示例 using System Collections using System
  • Linux 磁盘命令工具 比df更好用

    对于分析磁盘使用情况 有两个非常好用的命令 du 和 df 简单来说 这两个命令的作用是这样的 du 命令 它是英文单词 disk usage 的简写 主要用于查看文件与目录占用多少磁盘空间 df 命令 它是英文单词 disk free 的
  • python爬取证券之星网站

    周末无聊 找点乐子 coding utf 8 import requests from bs4 import BeautifulSoup import random import time 抓取所需内容 user agent Mozilla
  • 安卓逆向学习-Crack01 学习记录

    Crack01 学习记录 要感谢京峰教育 资料下载 https download csdn net download m0 47210241 85053839 利用jadx gui打开 分析代码 package com zhy editVi
  • nodejs封装api

    安装了nodeJs 执行 安装淘宝镜像 npm install g cnpm registry https registry npm taobao org 安装 yarn 我使用这个 淘宝镜像总是莫名其妙各种bug npm install
  • aix安装 php,CNESA

    aix安装samba服务器可以使用两种方式安装 一种是使用rpm包进行安装 一种是使用源码编译安装 一 使用samba的rpm包进行安装 1 下载samba的rpm包 下载地址为http www bullfreeware com searc
  • C++笔记--线程间共享数据

    当线程在访问共享数据的时候 必须制定一些规矩 用来限定线程可访问的数据位 还有 一个线程更新了共享数据 需要对其他线程进行通知 从易用性的角度 同一进程中的多个线程进行数据共享 错误的共享数据使用是产生并发bug的一个主要原因 当涉及到共享
  • 为什么训练集用fit_transform()而测试集用transform()及sklearn.feature_extraction.text.CountVectorizer API详解

    真正讲明白的 https blog csdn net yyhhlancelot article details 85097656 API https scikit learn org stable modules generated skl
  • mysql+mybatis 批量插入与批量更新

    首先批量更新需要增加 数据库的链接必须加上但是数据库连接必须加上 allowMultiQueries true 属性 不然会报错 You have an error in your SQL syntax check the manual t
  • 各种源码下载地址(目前只有ffmpeg和nginx,libcurl,RapidJSON 文档)

    各种源码下载地址 目前只有ffmpeg和nginx libcurl RapidJSON 文档 ffmpeg源码下载地址 http ffmpeg org download html releases nginx源码下载地址 http hg n
  • H5监听移动端物理/浏览器返回键

    JavaScript没有监听物理返回键的API 所以只能使用 popstate 事件监听 工具类如下 export function handleBrowserGoBack back console log back pushHistory
  • 论文阅读——基于深度学习智能垃圾分类

    B Fu S Li J Wei Q Li Q Wang and J Tu A Novel Intelligent Garbage Classification System Based on Deep Learning and an Emb
  • su命令切换用户输入密码后,提示:鉴定故障

    在终端通过su命令切换用户输入密码后 提示 鉴定故障 这是因为在安装linux系统时未设置root用户密码造成的 需要重新设置密码后再切换用户 具体操作命令如下 设置root用户密码 sudo passwd root 切换用户 su
  • 三步搞定ABAP DOI操作EXCEL

    ABAP可以使用OLE与DOI两种方式实现操作EXCEL 使用OLE时 每个单元格的值和样式都需要写代码实现 特别是对于不规则的格式 代码量巨大 而DOI是从服务器已经上传的EXCEL模板中下载模板然后打开修改实现数据保存 当然 也可以直接
  • springboot中的kafka的KafkaTemplate的配置类

    package com lf report utils import org apache kafka clients producer ProducerConfig import org apache kafka common seria
  • opencv 智能答卷识别系统(一)

    目标 这里是2 智能答卷之别系统二 识别答卷答案 识别准号证号码 识别姓名 识别试卷类别 试卷是有标记的 如试卷上方的黑框 排序结构 使用c 的标准排序算法 struct Ruley bool operator const Rect a1
  • 华为OD机试真题-座位调整-2023年OD统一考试(B卷)

    题目描述 疫情期间课堂的座位进行了特殊的调整 不能出现两个同学紧挨着 必须隔至少一个空位 给你一个整数数组 desk表示当前座位的占座情况 由若干 0 和 1 组成 其中 0 表示没有占位 1 表示占位 在不改变原有座位秩序情况下 还能安排
  • C#获取当前路径 -- 7种方法

    目录 一 代码 二 路径区别 三 参考文献 一 代码 推荐使用第3种 internal static class Program static void Main 获取模块的完整路径 string path1 System Diagnost
  • C++:入门学习C++,它在C的基础上做了哪些修改?

    文章目录 命名空间 缺省参数 函数重载 引用 引用作为函数返回值 引用的收益 引用和指针的区别 引用和指针的对比 内联函数 内联函数的特性 内联函数和宏的关系 指针空值问题 命名空间 首先看这样的代码 include