栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++入门

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++入门

第一个C++程序 
#include 
using namespace std;
int main(){
	cout << "hello world" << endl;
}
命名空间 C语言中产生的命名冲突问题
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int rand = 0;
int main()
{
	printf("%dn", rand);
	return 0;
}

  •  这里的rand和C标准库的stdlib.h中的rand函数会发生命名冲突,rand重定义
 命名空间的定义
#include 
namespace bit
{
	int rand = 0;
}

int main()
{
	printf("%dn", rand);//这里打印的是rand函数的地址
	printf("%dn", bit::rand);
	return 0;
}
  • namespace是C++中的一个关键字,
  • namespace给一段空间起了一个名字叫bit,并且将int rand = 0:定义在这段空间中
  • ::叫做域作用限定符,就是配合命名空间一起使用的,使编译器优先在这个空间里访问
 命名空间的使用 方式一:加命名空间名称及作用域限定符
#include 
namespace ly
{
	// 自动识别类型
	int i = 11;
	double d = 22.33;
}

int main()
{
	printf("%d,%lf", ly::i, ly::d);
	return 0;
}
  • 编译器一般先访问局部域中找,再从全局域找,再从自定义域中找, 
方式二:使用using将命名空间中成员引入
#include 
using std::cout;
using std::endl;
int main()
{
	// 自动识别类型
	int i = 11;
	double d = 22.33;

	// >>流提取
	cout << i << "," << d << std::endl;

	return 0;
}
  •  这里使用C++中的using关键字,只会直接把C++中的std命名空间中的cout和endl成员展开 
 方式三:使用using namespace 命名空间名称引入
#include 
using namespace std;
int main()
{
	// 自动识别类型
	int i = 11;
	double d = 22.33;

	// >>流提取
	cout << i << "," << d << std::endl;

	return 0;
}
  • 这里使用C++中的using关键字,会直接把C++中的std命名空间中的所有成员展开
命名空间的补充说明
  1. C++标准库的东西都放到std里了
  2. 项目中,尽量不要using namespace std;
  3. 在项目中更常用的是,方法二:指定名空间访问+展开常用
  4. 但是在日常练习的时候推荐使用:方法三:using namespace std;
  5. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间
 命名空间支持嵌套
#include 
namespace sql
{
	int a = 0;
	namespace bit
	{
		int rand = 0;
		void func()
		{
			printf("func()n");
		}

		struct TreeNode
		{
			struct TreeNode* left;
			struct TreeNode* right;
			int val;
		};
	}
}

int main()
{
	printf("%pn", rand);
	printf("%dn", sql::bit::rand);

	sql::bit::func();

	struct sql::bit::TreeNode node;

	return 0;
}


 缺省参数 缺省参数是声明或定义函数时为函数的参数指定一个默认值。 在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参
#include
using namespace std;
void TestFunc(int a = 0) {
	cout << a << endl;
}
int main()
{
	TestFunc(); // 没有传参时,使用参数的默认值
	TestFunc(10); // 传参时,使用指定的实参
}

 

  • 这里的第一个TestFunc();没有传参是缺省参数的,发生了int a = 0;
  • 这里的第二个TestFunc();正常传实参,形参正常接收,不是缺省参数
全缺省参数 
#include
using namespace std;
void TestFunc(int a = 10, int b = 20, int c = 30) {
	cout << "a = " << a;
	cout << " b = " << b;
	cout << " c = " << c << endl;
}
int main()
{
	TestFunc(1);
	TestFunc(1,2);
	TestFunc(1,2,3);

	TestFunc();
	return 0;
}

 半缺省参数 

错误用法一

  •  半缺省参数必须从右往左依次来给出,是不能间隔着给

错误用法二 

  • 半缺省函数传参时,只能从左向右传参

 错误用法三

  •  缺省参数不能在函数声明和定义中同时出现
  • 分离定义时:只需要声明给缺省参数,定义不需要给缺省参数
注意: 如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

正确用法

#include
using namespace std;
void TestFunc(int a, int b = 20, int c = 30) {
	cout << "a = " << a;
	cout << " b = " << b;
	cout << " c = " << c << endl;
}
int main()
{
	TestFunc(1,2);
	return 0;
}

缺省参数的实际用途  栈的初始化(数组实现)+没加缺省参数(C语言实现)
#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}Stack;

void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL) {
		return;
	}
	ps->top = 0;
	ps->capacity = 4;
}

int main()
{
	Stack s;
	StackInit(&s);//初始化

	return 0;
}

 栈的初始化(数组实现)+缺省参数(C++实现)
#include
using namespace std;

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}Stack;

void StackInit(Stack* ps,int capacity = 4)
{
	if (ps == NULL) {
		return;
	}
	ps->a = (STDataType*)malloc(sizeof(STDataType) * capacity);
	if (ps->a == NULL) {
		return;
	}
	ps->top = 0;
	ps->capacity = capacity;
}

int main()
{
	Stack s;
	StackInit(&s);//初始化

	return 0;
}
  • C++引入了缺省参数的概念,提高了函数的灵活性,代码的简洁性
 缺省参数的补充说明
  1. 缺省值必须是常量或者全局变量
  2. C语言不支持 缺省参数 ,或者说C语言的编译器不支持
函数重载 函数重载 是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的 形参列表(参数个数 或 类型 或 顺序)必须不同, 常用来处理实现功能类似数据类型不同的问题
#include
using namespace std;
//两个函数参数类型不同
int Add(int left, int right)
{
	return left + right;
}

double Add(double left, double right)
{
	return left + right;
}

//两个函数参数顺序不同
void func(int i, char ch)
{
	cout << "void func(int i, char ch)" << endl;
}

void func(char ch, int i)
{
	cout << "void func(char ch, int i)" << endl;
}

//两个函数参数的个数不同
int sub(int a, int b, int c)
{
	return a - b - c;
}

int sub(int a, int b)
{
	return a - b;
}

int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(1.1, 2.2) << endl;

	func(1, 'a');
	func('a', 1);

	cout << sub(1, 2) << endl;
	cout << sub(1, 2, 3) << endl;
	return 0;
}


 错误使用一

#include
using namespace std;
double Add(double left, double right)
{
	return left + right;
}

double Add(double right, double left)
{
	return left + right;
}

int main()
{
	cout << Add(1.1, 2.2) << endl;
	return 0;
}
  • 因为是同类型的,所以顺序改变是没有用的,编译器是不能区分的

错误使用二

#include
using namespace std;
short Add(short left, short right)
{
	return left + right;
}

int Add(short left, short right)
{
	return left + right;
}

int main() {
	cout << Add(1, 2) << endl;
	cout << Add(1, 2) << endl;
	return 0;
}
  • 返回值不同是不够成重载的,编译器是不能区分的

函数重载的优势
#include
using namespace std;
//两个函数类型不同
//void Swapi(int* p1, int* p2)
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//void Swapd(double* p1, double* p2)
void Swap(double* p1, double* p2)
{
	double tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

int main()
{
	int a = 1, b = 2;
	double c = 1.1, d = 2.2;
	Swap(&a, &b);
	Swap(&c, &d);

	cout << a;
	cout << c;

	return 0;
}
  • 在C++中出现了函数重载使代码更加简洁,看上就像是在调用同一函数一样
  • 而C语言是没有函数重载的概念,写起来就会麻烦点,代码也不简洁
关于为什么C++支持函数重载,而C语言不支持函数重载的探讨

  •  这一堆报错就是gcc编译器中的函数重定义

  •  这要是用g++编译器,就没有问题

首先我们知道程序翻译的最后一个过程是链接

  • 链接:.o的目标文件合并到一起,符号表的合并,其次还需要找一些只给声明函数变量地址,
  • 在C++中有一套函数名修饰规则 ,这也是C++能使用函数重载的前提,
  • 函数名修饰规则:会根据函数的形参列表(参数个数 或 类型 或 顺序)不同,生成不同的函数名
  • 在call这条汇编中会压入返回地址,如果声明没有就会去找
  • 简单的说:C++中函数名相同但形参列表那些不同的,通过函数名修饰规则,编译器就能正确识别

  •  而上面的代码gcc编译器想要编译通过,只能改代码,改函数名,
  • C语言没有函数名修饰规则,不支持函数重载
在vs2019中C++写的程序,调用C语言写的库
  •  最下面的代码中的extern"C"意思是链接查找
在vs2019中C语言写的程序,调用C++写的库 

  • extern "C" 需要写在C++写的库中
  •  C++是兼容C语言的,而C语言是不支持C++的,
    不支持extern "C"这样的用法,需要用条件编译
引用 引用 不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
#include
using namespace std;
int main()
{
	int a = 0;
	int& b = a;
	cout << &b << endl; // 取地址
	cout << &a << endl; // 取地址
	a++;
	b++;
	cout << b << endl;//2
	cout << a << endl;//2
	return 0;
}

  •  这里的&不是取地址,而是C++的引用,注意和C语言区分
引用的三个特性
  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 一个引用只能是一个变量的别名
#include
using namespace std;
int main()
{
	int a = 1;
	// int& b;  // 1、引用在定义时必须初始化

	int& b = a; // 2、一个变量可以有多个引用
	int& c = a;
	int& d = c;
	++a;

	int x = 10;
	// 3、引用一旦引用一个实体,再不能引用其他实体
	b = x; // 这里b不是x的别名,是把x赋值给a的别名b

	return 0;
}
引用的使用场景 场景一:做参数

单链表的头插+没加引用(C语言实现)

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

SLTNode* BuySLTNode(SLTDataType x)
{
	//....
}

void SListPushFront(SLTNode** pphead, SLTDataType x)
{
	//开节点
	SLTNode* newnode = BuySLTNode(x);//....
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}

int main()
{
	SLTNode* phead = NULL;//头指针
	SListPushFront(&phead, 1);
	SListPushFront(&phead, 2);
	return 0;
}

单链表的头插+加引用(C++语言实现)

#include
using namespace std;
typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode,* SLTNode_;

SLTNode* BuySLTNode(SLTDataType x)
{
	//....
}

void SListPushFront(SLTNode_& pphead, SLTDataType x)
{
	//开节点
	SLTNode* newnode = BuySLTNode(x);//....
	if (pphead == NULL)
	{
		pphead = newnode;
	}
	else
	{
		newnode->next = pphead;
		pphead = newnode;
	}
}

int main()
{
	SLTNode* phead = NULL;
	SListPushFront(phead, 1);
	SListPushFront(phead, 2);
	return 0;
}
  • C++引入了引用,单链表传参就不用传地址了,引用使代码更加简洁
场景二:大对象传参 
#include
using namespace std;
struct A { int a[10000]; };

void TestFunc1(A aa) {}
void TestFunc2(A& aa) {}

void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000000; ++i)
		TestFunc1(a);
	size_t end1 = clock();

	// 以引用作为函数参数`````
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000000; ++i)
		TestFunc2(a);
	size_t end2 = clock();

	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();
	return 0;
}

  •  引用是不会开空间的,只是已存在变量的别名,不会发生形参实例化,
场景三:传引用返回 

首先说一下传值返回

#define _CRT_SECURE_NO_WARNINGS 1
#include
int Count1()
{
	int n = 0;
	n++;
	return n;
}

int Count2()
{
	static int n = 0;
	n++;
	return n;
}

int main() {
	int ret1 = Count1();
	printf("ret1第一次:%dn", ret1);
	printf("ret1第二次:%dn", ret1);
	int ret2 = Count2();
	printf("ret2第一次:%dn", ret2);
	printf("ret2第二次:%dn", ret2);
	return 0;
}

  •  这里是传值返回,是把n的值放在寄存器中,然后通过寄存器返回值

那我们再看一下传引用返回 

#include
using namespace std;
int& Count1()
{
	int n = 0;
	n++;
	return n;
}

int& Count2()
{
	static int n = 0;
	n++;
	return n;
}

int main() {
	int& ret1 = Count1();
	printf("ret1第一次:%dn", ret1);
	printf("ret1第二次:%dn", ret1);
	int& ret2 = Count2();
	printf("ret2第一次:%dn", ret2);
	printf("ret2第二次:%dn", ret2);
	return 0;
}

  • Count1函数中的n是在栈区开辟空间的,而这里是传引用返回,返回的是n这个变量,而Count1函数栈帧被销毁之后,自然变量n就不在了,自然就是随机值
  • 注意:这里ret1第一次打印是1,实属巧合,再打印第二次,结果是随机值(如果栈帧结束,系统会清理栈帧并置成随机值),因为printf函数也是会形成栈帧,就可能会把Count1函数之前形成栈帧的那块空间覆盖掉
  •  被static修饰的变量是放在静态区的,Count2函数返回对象是出了作用域还存在,自然两次打印结果都是1

结论:

  • 出了作用域还在的——>传引用返回
  • 出了作用域被销毁——>传值返回
引用与const产生的权限问题 

权限不能放大,但是可以缩小,权限是支持平移的

#include
using namespace std;
int main()
{
	int a = 10;
	int& b = a;
	cout << typeid(a).name() < 

  •  对于同类型的变量,被const修饰之后,由之前的可读可写变成了可读不可写
  • 对于不同类型的变量,通常会发生隐式类型转换,并不会改变原变量类型,中间都会产生一个临时变量 
  • 临时变量具有常性,所以临时变量只可读,不可写,这也是变量ii不能赋值给dd,但却可以赋值给rdd的原因,也出现了const int& x  = 10;这样的代码

编程好习惯:如果使用引用传参,函数内如果不改变这个引用,那么建议尽量用const引用传参 


 引用的实际用途

栈的访问(数组实现)+没加传引用返回(C语言实现)

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}Stack;

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top);
	return ps->a[ps->top - 1];
}

void StackInit(Stack* ps)
{
	//...
}

void StackDestory(Stack* ps)
{
	//...
}

int main()
{
	Stack s;
	StackInit(&s);
	printf("%dn", StackTop(&s));
	StackDestory(&s);
	return 0;
}

 栈的访问(数组实现)+加引用返回(C++语言实现)

#include
#include
using namespace std;

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}Stack;

STDataType& SLAt(Stack& s, STDataType pos)
{
	assert(pos >= 0 && pos <= s.capacity);

	return s.a[pos];//返回的就是栈中的值
}

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top);
	return ps->a[ps->top - 1];
}

void StackInit(Stack& ps)
{
	//...
}

void StackDestory(Stack& ps)
{
	//...
}

int main()
{
	Stack s;
	StackInit(s);
	cout << SLAt(s, 0) << endl;
	SLAt(s, 0)++;
	cout << SLAt(s, 0) << endl;
	for (int i = 0; i < s.capacity; i++)
	{
		cout << SLAt(s, i) << " ";
	}
	StackDestory(s);
	return 0;
}
  • C++引用是可以直接对栈里任意位置并进行++等操作,
  • 这个栈是数组实现的,是用malloc在堆上申请空间的,出了作用域还存在,可以用传引用返回
  • 传参用引用接收,返回用引用返回,这些都是可以提高效率的
引用和指针的关系

从使用场景来说

  • 引用和指针使用场景基本一样,但是链表的链式结构是引用无法代替的,只能使用指针

从语法特性来说

  • 引用定义必须初始化,而指针不需要
  • 一个变量可以有多个引用,但是一个引用只能是一个变量的别名,指针随意
  • sizeof(引用)的大小是引用类型的大小,sizeof(指针)的大小是4或8
  • 有多级指针,但是没有多级引用
  • 指针更强大,更危险,更复杂,而引用相对局限一些,更安全,更简单

从底层原理来说

#include
using namespace std;
int main()
{
	// 语法的角度,ra没有开空间
	int a = 10;
	int& ra = a;
	ra = 20;

	// 语法的角度,pa开辟了4或8个字节的空间
	int* pa = &a;
	*pa = 20;

	return 0;
}

  •  lea是一条取地址的汇编命令,底层原理中引用是用指针实现的
内联函数 为什么要引入内联函数
  • C++中引入内联函数主要是为了解决宏的问题或者说,优化C语言中的宏

宏的优点

  • 代码维护性强,宏函数效率高(减少栈帧的建立)

宏的缺点

  • 可读性差,没有类型安全检查,不方便调试(预处理就会被替换掉)

  •  release版本的优化太强,可能在反汇编中什么都看不见
  • 如果要在Debug版本下,通过反汇编来查看inline的作用,需要进行上面的配置
#include
using namespace std;
inline void Add(int a, int b)
{
	int c = a + b;
	printf("Add(%d, %d)->%dn", a, b, c);
}

void Sub(int a, int b)
{
	int c = a - b;
	printf("Sub(%d,%d)->%dn", a, b, c);
}

int main()
{
	Add(1, 2);
	Sub(1, 2);

	return 0;
}

 

  •  以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧 的开销,内联函数提升程序运行的效率。
inline函数三大特性
  1. inline是一种以空间换时间的做法,省去调用函数开销,所以代码很长,或者有递归的函数不适宜作为内联函数
  2. inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内部实现代码指令长度比较长(10行左右,不同编译器不同)/递归等等,编译器优化时会忽略掉内联,这时inline将不起作用
  3. inline不建议声明和定义分离,如果分离就会导致链接错误,因为inline被展开,就没有函数地址,链接就会找不到
#include
using namespace std;
inline void func(int a, int b)
{
	cout << a + b << endl;
}

inline void funcc(int a, int b)
{
	cout << a + b << endl;
	cout << a + b << endl;
}

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

  •  这里的cout也是一种函数,调用需要很多指令,所以用inline修饰的函数中2行及2行以上的cout就不会展开了
  • 如果funcc函数展开的话,编译出来的可执行程序可能会很大,
    比如:调用1w次funcc函数
    • 1w次调用+100行指令
    • 1w*100行指令
 auto关键字(C++11)
#include
using namespace std;
int TestAuto()
{
	return 10;
}
int main()
{
	int a = 10;
	auto b = a; // 自动推导类型
	auto c = 'a';
	auto d = TestAuto();
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;

	return 0;
}

  •  赋值给auto关键字修饰的变量,auto关键字会自动推导类型
为什么C++会引入auto关键字
  • 随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在:
    • 类型难于拼写,含义不明确导致容易出错
#include 
#include 
int main()
{
     std::map m{ 
     { "apple", "苹果" }, { "orange", "橙子" }, 
     {"pear","梨"} };
     std::map::iterator it = m.begin();
     while (it != m.end())
     {
         //....
     }
     return 0; 
}
  • 这里的 std::map::iterator 是一个类型
C语言使用typedef给类型取别名出现的问题
#include
typedef char* pstring;
int main()
{
     const pstring p1; // 编译成功还是失败?
     const pstring* p2; // 编译成功还是失败?
     return 0; 
}
  • 这里的pstring类型,对于设计者来说清楚,但是对于使用者来说pstring类型含义不明确
auto能推导的场景

场景一:auto与指针和引用结合起来使用

#include
using namespace std;
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;

    return 0;
}
 
  •  用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
场景二:在同一行定义多个变量 
#include
using namespace std;
void TestAuto()
{
    auto a = 1, b = 2;
    auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
int main()
{
    TestAuto();
    return 0;
}
  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对 第一个类型进行推导,然后用推导出来的类型定义其他变量
auto不能推导的场景  场景一:auto不能作为函数的参数
#include
using namespace std;
// 此处代码编译失败,auto不能作为形参类型,
//因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{
    //...
}
int main()
{
    TestAuto(1);
    return 0;
}
场景二:auto不能直接用来声明数组
#include
using namespace std;
// 此处代码编译失败,auto不能作为形参类型,
//因为编译器无法对a的实际类型进行推导
void TestAuto()
{
    int a[] = { 1,2,3 };
    auto b[] = { 4,5,6 };
}

int main()
{
    TestAuto();
    return 0;
}

 基于范围的for循环(C++11)
#include
using namespace std;
void TestFor_CPP()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for (auto& e : array)//这里只能用引用
        e *= 2;

    for (auto e : array)
        cout << e << " ";
}

void TestFor_C()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
        array[i] *= 2;

    for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
        printf("%d ", array[i]);
}

int main()
{
    TestFor_C();
    cout << endl;
    TestFor_CPP();
    return 0;
}
  • C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
  • 迭代器的循环与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环
范围for的使用条件 条件一:for循环迭代的范围必须是确定的
#include
using namespace std;
void TestFor(int array[])
{
    for (auto& e : array)//范围不明确
        cout << e << endl;
}

int main()
{
    int a[] = { 1, 2, 3, 4, 5, 6 };
    TestFor(a);
    return 0;
}
  • for循环迭代的范围,对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。
指针空值nullptr(C++11) 

C语言中的NULL其实是一个关键字,在stddef.h中

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

#include
using namespace std;
void f(int)
{
	cout << "f(int)" << endl;
}

void f(int*)
{
	cout << "f(int*)" << endl;
}

int main()
{
	int* p = NULL;
	f(0);
	f(NULL);
	f(p);

	// C++11 nullptr 关键字 替代NULL
	f(nullptr);
	int* ptr = nullptr;

	return 0;
}

  • 在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下,将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。所以引入了nullptr
 对指针空值nullptr的补充说明
  • 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的
  • 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  • 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1033235.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号