C++ 基础入门-命名空间、c++的输入输出、缺省参数、函数重载、引用、内联函数超详细讲解

这篇文章主要对c++的学习做一个基础铺垫,方便后续学习。主要通过示例讲解命名空间、c++的输入输出cout\cin,缺省参数、函数重载、引用、内联函数,auto关键字,for循环,nullptr以及涉及到的周边知识,面试题等。为后续类和对象学习打基础。

目录

什么是c++?

 C++关键字(C++98)

一、命名空间

命名空间定义

1.正常的命名空间的定义

域:

命名空间域

展开命名空间

 std:所有C++库命名空间

命名空间可以嵌套

二、C++输入&输出

<<:

>>:

std命名空间的使用惯例:

三、缺省参数

缺省/默认参数:

全缺省(给出所有参数):

半缺省(从右往左连续给):

注意:

四、函数重载

C++支持函数重载的原理--名字修饰(name Mangling) 

*有关于预处理、编译、汇编、链接的详解,可见:

五、引用(别名)

引用概念

使用方法:

引用特性:

1.引用必须初始化

​编辑

2.引用定义后,不能改变指向

3.一个变量可以有多个引用,多个别名

常引用:只能权限缩小,不能权限扩大

使用场景:

 1、做参数(a、输出型参数 b、对象比较大,减少拷贝,提高效率) 这些效果,指针也可以,但是引用更方便

a、输出型参数

b、对象比较大,减少拷贝,提高效率

2、做返回值(a、修改返回对象 b、减少拷贝提高效率)

面试题:引用和指针的区别:

六、内联函数

面试题:宏的优缺点

内联函数概念:

查看方式:

​编辑inline 特性

inline是一种以空间换时间的做法

内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求:

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

为什么要防止链接冲突?

在.h未做声明和定义分离时,为什么会出现这样的报错?

以下有三种解决方案:

1、当然就是声明和定义的分离

2、加静态static,修饰函数时,与全局函数相比,有链接属性,只在当前文件可见。(不会进符号表)

3、使用内联(内联也不支持声明与定义分离)

总结:若需在.h中定义函数:

七、auto关键字(C++11)

auto简介

auto使用示例:

auto不能推导的场景

八、基于范围的for循环(C++11)

范围for的语法

范围for的使用条件

九、指针空值nullptr(C++11)

注意:


什么是c++?

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

 C++关键字(C++98)

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

以下是学习c++时会用到的关键字。

一、命名空间

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

命名空间定义

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

1.正常的命名空间的定义

域:

::域作用限定符

编译器搜索原则:

不指定域:

首先在当前局部域搜索再到全局域。

指定域:

如果指定了域,直接去指定域搜索

示例:

#include<stdio.h>int x = 0;int main()
{int x = 1;printf("hello world\n");printf("%d\n", x);printf("%d\n", ::x);return 0;
}

输出结果:

hello world
1
0

命名空间域

示例:

 proj1是命名空间的名字,一般开发中是用项目名字做命名空间名。

 命名空间中可以定义变量/函数/类型

#include<stdio.h>
#include<stdlib.h>namespace proj1
{int rand = 0;int x = 0;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};
}namespace proj2
{int x = 1;
}

int main()
{printf("%d\n", proj1::x);printf("%d\n", proj2::x);printf("%p\n", rand);printf("%d\n", proj1::rand);printf("%d\n", proj1::Add(1,2));return 0;
}

输出结果:

0

1
00007FFD91E94AD0
0
3

展开命名空间

同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

示例:

Queue.h

namespace xxx
{struct Node{int val;struct Node* next;};struct Queue{struct Node* head;struct Node* tail;int size;};void Init(struct Queue* pq);void Push(struct Queue* pq, int x);
}

List.h

#pragma oncenamespace xxx
{struct QNode{int val;struct QNode* next;struct QNode* prev;};void Init(struct QNode* phead);void PushBack(struct QNode* phead, int x);
}
#include<stdio.h>
#include"List.h"
#include"Queue.h"// 展开命名空间
using namespace xxx;int main()
{//printf("hello world\n");struct QNode node1;struct xxx::QNode node2;struct xxx::QNode node3;return 0;
}

此时就会通过:using namespace xjh;

到我自定义的命名空间去寻找展开

 std:所有C++库命名空间

示例:


#include<iostream>
using namespace std;int main()
{std::cout << "hello world" << std::endl;std::cout << "hello world" << std::endl;std::cout << "hello world" << std::endl;std::cout << "hello world" << std::endl;std::cout << "hello world" << std::endl;std::cout << "hello world" << std::endl;cout << "hello world" << endl;return 0;
}

命名空间可以嵌套

示例:

#include<iostream>
#include<stdio.h>
using std::cout;
using std::endl;
namespace N1
{int a;int b;int Add(int left, int right){return left + right;}namespace N2{int c;int d;int Sub(int left, int right){return left - right;}}
}int main()
{int ret = N1::Add(1, 2);int sub = N1::N2::Sub(5, 6);cout << ret << endl;cout << sub << endl;}

输出结果:

3
-1

二、C++输入&输出

说明:

1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件 以及按命名空间使用方法使用std。

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

在< iostream >头文件中。

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

4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。 C++的输入输出可以自动识别变量类型

5. 实际上 cout 和 cin 分别是ostreamistream类型的对象,>>和也涉及运算符重载等知识,我们这里只是简单学习他们的使用。

<<:

1.左移 相当于扩大2倍

2.流插入 自动识别类型

示例:

#include<iostream>
using namespace std;int main()
{// 1、左移int i = 100;i = i << 1;const char* str = "hello world";char ch = '\n';2、流插入 自动识别类型cout << str << i << ch << endl;printf("%s%d %c", str,i,ch);return 0;
}

输出结果:

hello world200

hello world200

>>:

右移:缩小2倍

流提取:

示例:

#include<iostream>
using namespace std;int main()
{int i;const char* str = "hello world";char ch;// 右移流提取cin >> i >> ch;cout << str << i << ch << endl;return 0;
}

输出结果:

50 \n(这个是我自己输入的)
hello world50\

ps:关于cout和cin还有很多更复杂的用法,比如控制浮点数输出精度,控制整形输出进制格式等 等。因为C++兼容C语言的用法,这些又用得不是很多,我们这里就不展开学习了。

以下提供一个例子:

#include<iostream>
using namespace std;int main()
{double d = 1.11111111;printf("%.2lf\n", d);cout << d << endl;return 0;
}

输出结果:

1.11
1.11111

std命名空间的使用惯例:

std是C++标准库的命名空间,如何展开std使用更合理呢?

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

2. using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对 象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模 大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。

三、缺省参数

缺省参数概念

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

这里与c语言的区别:c语言必须要和申明函数的格式相同。

缺省/默认参数:

示例:

#include<iostream>
using namespace std;
void Func(int a = 0)
{cout << a << endl;
}int main()
{Func(1);Func();return 0;
}

输出结果:

1

0

全缺省(给出所有参数):

示例:

#include<iostream>
using namespace std;//全缺省
void Func(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}int main()
{Func(1, 2, 3);Func(1, 2);Func(1);Func();return 0;
}

输出结果:

a = 1
b = 2
c = 3

a = 1
b = 2
c = 30

a = 1
b = 20
c = 30

a = 10
b = 20
c = 30

半缺省(从右往左连续给):

示例:

#include<iostream>
using namespace std;//半缺省 从右往左连续给
void Func(int a, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}int main()
{Func(1, 2, 3);Func(1, 2);Func(1);return 0;
}

输出结果:

a = 1
b = 2
c = 3

a = 1
b = 2
c = 30

a = 1
b = 20
c = 30

注意:

1. 半缺省参数必须从右往左依次来给出,不能间隔着给,如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

 //a.hvoid Func(int a = 10);// a.cppvoid Func(int a = 20){}

2. 缺省参数不能在函数声明和定义中同时出现

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

4. C语言不支持(编译器不支持)

四、函数重载

 C语言不允许同名函数
 CPP语言允许同名函数,要求:函数名相同,参数不同,构成函数重载

 1、参数类型的不同
 2、参数个数不同
 3、参数顺序不同(本质还是类型不同)

示例:

#include<iostream>
using namespace std;// _Z3Addii
void Add(int left, int right);
//{
//	cout << "int Add(int left, int right)" << endl;
//	return left + right;
//}// _Z3Adddd
double Add(double left, double right);
//{
//	cout << "double Add(double left, double right)" << endl;
//	return left + right;
//}void f(int a, char b);
void f(char b, int a);int main()
{Add(1, 2);Add(1.1, 2.2);//f(1, 'a'); // call f(?)//f('a', 1); // call f(?)// f(1, 'a'); // call _Z1fic(?)f('a', 1); // call _Z1fci(?)return 0;

C++支持函数重载的原理--名字修饰(name Mangling) 

为什么C++支持函数重载,而C语言不支持函数重载呢?

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。

*有关于预处理、编译、汇编、链接的详解,可见:

Linux基础 - yum、rzsz、vim 使用与配置、gcc/g++的详细解说以及预处理、编译、汇编、链接的详细步骤ESc 、iso_linux rzsz-CSDN博客

C语言不支持重载  :

因为链接时,直接用函数名去找地址,有同名函数,区分不开

CPP如何支持的呢?

函数名修饰规则,名字中引入参数类型,各个编译器自己实现了一套

五、引用(别名)

引用概念

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

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

使用方法:

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

改变引用时,原值也会随之改变

示例:


#include<iostream>
using namespace std;int main()
{int a = 0;// 引用,b就是a的别名int& b = a;cout << &b << endl;cout << &a<< endl;b++;a++;cout << b << endl;cout << a << endl;int& c = a;int& d = c;d++;cout << d << endl;return 0;
}

输出结果:

当引用做参数时,会比指针方便很多:

因为,传的就是原值的别名,因此连带原值直接修改

指针和引用功能类似,重叠,但是引用不能完全替代指针,因为引用定义后,不能改变指向

示例:对比以下两种 传的就是原值的别名,因此连带原值直接修改

#include<iostream>
using namespace std;//做参数
void Swap(int* a, int *b)
{int tmp = *a;*a = *b;*b = tmp;
}void Swap(int& a, int &b)
{int tmp = a;a = b;b = tmp;
}int main()
{int x = 0, y = 1;Swap(&x, &y);cout << "x = " << x << " y = " << y << endl;Swap(x, y);cout << "x = " << x << " y = " << y << endl;return 0;
}

引用特性:

1.引用必须初始化

示例:

2.引用定义后,不能改变指向

示例:

C++的引用,对指针使用比较复杂的场景进行一些替换,让代码更简单易懂,但是不能完全替代指针。


引用不能完全替代指针原因:引用定义后,不能改变指向

特别是当我们写一个链表时:因为引用不能改变指向,因此在这里根本不适用。


struct Node
{struct Node* next;struct Node* prev;int val;
};//void PushBack(struct Node* phead, int x)
//{
//	// phead = newnode;
//}//void PushBack(struct Node** pphead, int x)
//{
//	// *pphead = newnode;
//}void PushBack(struct Node*& phead, int x)
{//phead = newnode;
}int main()
{struct Node* plist = NULL;return 0;
}typedef struct Node
{struct Node* next;struct Node* prev;int val;
}LNode, *PNode;void PushBack(PNode& phead, int x)
{//phead = newnode;
}//void PushBack(SeqList* ps, int x);
//void PushBack(SeqList& ps, int x);
//{}int main()
{PNode plist = NULL;return 0;
}

3.一个变量可以有多个引用,多个别名

常引用:只能权限缩小,不能权限扩大

示例:

#include<iostream>
using namespace std;void TestConstRef()
{const int a = 10;//int& ra = a;   // 该语句编译时会出错,a为常量const int& ra = a;// int& b = 10; // 该语句编译时会出错,b为常量const int& b = 10;double d = 12.34;//int& rd = d; // 该语句编译时会出错,类型不同const int& rd = d;
}

并且可用于:函数传参时,防止传的值不能被修改

使用场景:

 1、做参数(a、输出型参数 b、对象比较大,减少拷贝,提高效率)
 这些效果,指针也可以,但是引用更方便

示例:

a、输出型参数

b、对象比较大,减少拷贝,提高效率

#include<iostream>
#include <time.h>
using namespace std;
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void main()
{A a;// 以值作为函数参数size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc1(a);size_t end1 = clock();// 以引用作为函数参数size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);size_t end2 = clock();// 分别计算两个函数运行结束后的时间cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

输出结果:可以观察出这时候使用引用更加提高效率

2、做返回值(a、修改返回对象 b、减少拷贝提高效率)

示例:

下面代码输出结果是什么?

为什么?

int& Add(int a, int b)
{int c = a + b;return c;
}
int main()
{int& ret = Add(1, 2);Add(3, 4);cout << "Add(1, 2) is :"<< ret <<endl;return 0;
}

注意:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用 引用返回,如果已经还给系统了,则必须使用传值返回。

面试题:引用和指针的区别:

语法:

1、引用是别名,不开空间,指针是地址,需要开空间存地址

2、引用必须初始化,指针不做必要

3、引用不能改变指向,指针可以

4、引用相对更加安全,没有空引用,但是有空指针,容易出现野指针,但是不容易出现野引用

5、sizeof 、 ++ 、解引用访问等方面的问题:(引用是引用类型大小,指针永远都是4 or 8)

底层:

汇编层面上,没有引用,都是指针,引用编译后也转换成指针了。

如图:

六、内联函数

面试题:宏的优缺点

宏的优点:

1、增强代码的复用性

2、提高性能

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

1. 常量定义 换用const enum

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

知识衍生:实现两个数相加的宏函数:

易错点
1、不是函数       #define ADD(int a, int b) return a+b;
2、分号#define ADD(a,  b) a+b;
3、括号控制优先级#define ADD(a,  b) ((a)+(b));
核心点:宏是预处理阶段进行替换

为什么加里面的括号:

#include<iostream>
using namespace std;
#define ADD(a, b) ((a)+(b))

// 为什么要加里面的括号?
int main()
{
    if (ADD(1, 2))
    {}

    ADD(1, 2) * 3;

    int x = 1, y = 2;
    ADD(x | y, x & y);  // (x|y + x&y) 
//   |,&的优先级小于+

    return 0;
}

 宏的缺点:
 1、语法复杂,坑很多,不容易控制
 2、不能调试
 3、没有类型安全的检查

由此我们引入内联:

内联函数概念:

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

示例

#include<iostream>
using namespace std;inline int Add(int a, int b)
{return a + b;
}int main()
{int ret1 = Add(1, 2) * 3;int x = 1, y = 2;int ret2 = Add(x | y, x & y);return 0;
}

如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。

查看方式:

1. 在release模式下,查看编译器生成的汇编代码中是否存在call Add

2. 在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不 会对代码进行优化,以下给出vs2022的设置方式)

此时再打开反汇编就可以观察到,函数在此时直接展开

inline 特性

inline是一种以空间换时间的做法

如果编译器将函数当成内联函数处理,在编译阶段,会 用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运 行效率。

内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求:

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

假如有一个func()函数

func()函数有100行     1w个地方调用这个函数

假设inline展开了        100*1w

假设inline不展开        100+1w

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

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

防止重复包含同一个头文件:

#pragma once = #ifdefy 1   

                           #define

                           #endif

为什么要防止链接冲突?

在.h未做声明和定义分离,会导致在多个.c文件中包含头文件时出现报错,多重定义的报错。

现在我在我的程序中写道这样的代码:

Stack.cpp

#include"Stack.h"

Stack.h

#include<iostream>int Add(int a, int b)
{return a + b;
}

Test.cpp

#include"Stack.h"int main()
{int ret = Add(1, 2);return 0;
}

运行后就会出现这样的报错:

在.h未做声明和定义分离时,为什么会出现这样的报错?

在stack.cpp与test.cpp中都包含一份Add函数(不构成函数重载),在链接时,会导致文件中出现两个一样的函数,从而重复定义出现报错。

以下有三种解决方案:
1、当然就是声明和定义的分离

2、加静态static,修饰函数时,与全局函数相比,有链接属性,只在当前文件可见。(不会进符号表)

进符号表:将地址加到符号表是为了让别人方便调用

Stack.h

#include<iostream>static int Add(int a, int b)
{return a + b;
}

Stack.cpp

#include"Stack.h"

Test.cpp

#include<iostream>inline int Add(int a, int b)
{return a + b;
}
3、使用内联(内联也不支持声明与定义分离)

Stack.h

#include<iostream>inline int Add(int a, int b)
{return a + b;
}

直接在.h中定义解决方案,不在.cpp中定义解决方案,同static,Add地址不会进符号表,inline直接展开。

总结:若需在.h中定义函数:

小函数用:inline(并且,在函数很长时,实际上也不会展开内联函数)

大函数用:static or 声明与定义分离

七、auto关键字(C++11)

类型别名思考

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

1. 类型难于拼写

2. 含义不明确导致容易出错

示例:

#include <string>
#include <map>
int main()
{std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange", 
"橙子" }, {"pear","梨"} };std::map<std::string, std::string>::iterator it = m.begin();while (it != m.end()){//....}return 0;
}

std::map::iterator 是一个类型,但是该类型太长了,特别容 易写错。

这里可以通过typedef给类型取别名

比如:

#include <string>
#include <map>
typedef std::map<std::string, std::string> Map;
int main()
{Map m{ { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} };Map::iterator it = m.begin();while (it != m.end()){//....}return 0;
}

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

typedef char* pstring;
int main()
{const pstring p1;    // 编译成功还是失败?const pstring* p2;   // 编译成功还是失败?return 0;
}

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

auto简介

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的 是一直没有人去使用它,大家可思考下为什么? C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一 个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

auto使用示例:

void func()
{
      //....
}

int main()
{
    int i = 0;
    int j = i;
    auto k = i;   
//自动识别i的类型加给k
    // auto x;  //auto不能做参数,返回值可做,但是很坑
    auto p1 = &i;
    auto* p2 = &i;
  //指针指向i 地址
    //auto* p3 = i;
    auto& r = i;

    void(*pf1)(int, int) = func; //函数指针
    auto pf2 = func;  

    return 0;
}

void func(int a, int b)
{}
int main()
{void(*pf1)(int, int) = func;auto pf2 = func;cout << typeid(pf1).name() << endl;cout << typeid(pf2).name() << endl;return 0;
}

运行这个代码:

auto自动识别类型

特别是当我们学到后期:

int main()
{std::map<std::string, std::string> dict;//std::map<std::string, std::string>::iterator it = dict.begin();auto it = dict.begin();cout << typeid(it).name() << endl;return 0;
}

虽然auto很方便很好用,但是大多数情况,我们最好还是自定义好类型

auto不能推导的场景

如下,若我们使用许多的auto,在我们的程序中会出现这样的问题:

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

返回值可做,但是很坑

void TestAuto(auto a)
{}
auto f2()
{auto ret = 1;return ret;
}auto f1()
{auto x = f2();return x;
}auto TestAuto()
{auto a = f1();return a;
}void func(int a, int b)
{}
int main()
{auto it2 = TestAuto();return 0;
}

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

void TestAuto()
{int a[] = {1,2,3};auto b[] = {4,5,6};
}

3. 为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

4. auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有 lambda表达式等进行配合使用。

八、基于范围的for循环(C++11)

范围for的语法

示例:

#include"Stack.h"
#include <string>
using namespace std;int main()
{//在C++98中如果要遍历一个数组,可以按照以下方式进行: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// 依次取数组中值赋值给e,自动迭代,自动判断结束for (auto& e : array){e *= 2;}cout << endl;for (auto e : array){cout << e << " ";}cout << endl;return 0;
}

运行结果:

2
4
6
8
10

4 8 12 16 20

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

范围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;// ……
}

在c语言中:NULL实际是一个宏且值为0

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

可以看到,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。

注意:

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

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

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

结语:

       随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。

       在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。         你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。

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

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

相关文章

知识付费APP开发指南:基于在线教育系统源码的技术详解

本篇文章&#xff0c;我们将探讨基于在线教育系统源码的知识付费APP开发的技术细节&#xff0c;帮助开发者和企业快速入门。 一、选择合适的在线教育系统源码 选择合适的在线教育系统源码是开发的关键一步。市场上有许多开源和商业化的在线教育系统源码&#xff0c;开发者需要…

参数高效的迁移学习在自然语言处理中的应用

人工智能咨询培训老师叶梓 转载标明出处 迁移学习技术&#xff0c;尤其是针对大型预训练模型的微调&#xff08;fine-tuning&#xff09;&#xff0c;在诸多下游任务中展现出了卓越的性能。然而&#xff0c;当面临众多任务时&#xff0c;传统的微调方法存在参数效率低下的问题…

Centos8.5.2111(1)之本地yum源搭建和docker部署与网络配置

由于后边可能要启动多个服务&#xff0c;避免服务之间相互干扰&#xff0c;本课程建议每个服务独立部署到一台主机上&#xff0c;这样做会导致资源占用过多&#xff0c;可能会影响系统的运行。服务器部署一般不采用GUI图形界面部署&#xff0c;而是采用命令行方式部署&#xff…

JavaWeb图书借阅系统

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 spring-mybatis.xml3.5 spring-mvc.xml3.5 login.jsp 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优…

【机器学习】——决策树以及随机森林

文章目录 1. 决策树的基本概念与结构1.1 决策树的构建过程 2. 决策树的划分标准2.1 信息增益&#xff08;Information Gain&#xff09;2.2 信息增益比&#xff08;Information Gain Ratio&#xff09;2.3 基尼指数&#xff08;Gini Index&#xff09;2.4 均方误差&#xff08;…

HJ50-四则运算:栈的运用、中缀表达式转后缀表达式并计算结果

文章目录 题目一、分析1.1表达式预处理1.2中缀表达式转后缀1.3 后缀表达式计算结果 二、答案 题目 一、分析 通过利用栈将中缀表达式转换为后缀表达式&#xff0c;在根据后缀表达式计算运算结果。由于包含负数操作数的情况&#xff0c;并且操作数位数不固定为1&#xff0c;因此…

USB 3.1 标准 B 型连接器的接口定义与引脚分配

连接器 USB 3.1 规范定义了以下连接器&#xff1a; 超速标准 A 插头和插座&#xff1b;超速标准 B 插头和插座&#xff1b;超速 Micro B 插头和插座&#xff1b;超速 Micro A 插头&#xff1b;超速 Micro-AB 插座。 所有超速连接器具有相同的配合接口并且彼此兼容。 下表列…

腾讯云SDK购买流程

音视频终端 SDK 需购买对应 License/套餐获得使用授权&#xff0c;本文将对购买 License/套餐的操作进行详细指引。 您可首先参考计费概述 确认您需要购买的内容&#xff0c;随后参考本文进行购买。本文仅提供 SDK 授权费用所需资源的购买&#xff0c;如果您需要使用其他相关云…

锦天云中秋之夜团圆家宴圆满成功

2024年9月7日&#xff0c;锦天云&#xff08;深圳&#xff09;计算机设备有限公司 在中国深圳成功举办了“融创智合•月满锦天 锦天云中秋之夜团圆家宴。本次盛会吸引了来自各行业的精英和合作伙伴&#xff0c;大家齐聚一堂&#xff0c;共同庆祝这一传统佳节&#xff0c;此次活…

SPI驱动学习七(SPI_Slave_Mode驱动程序框架)

目录 一、SPI_Slave_Mode驱动程序框架1. Master和Slave模式差别1.1 主设备 (Master)1.2 从设备 (Slave)1.3 示例 2. SPI传输概述2.1 数据组织方式2.2 SPI控制器数据结构 3. SPI Slave Mode数据传输过程4. 如何编写程序4.1 设备树4.2 内核相关4.3 简单的示例代码4.3.1 master和s…

K8S真正删除pod

假设k8s的某个命名空间如&#xff08;default&#xff09;有一个运行nginx 的pod&#xff0c;而这个pod是以kubectl run pod命令运行的 1.错误示范&#xff1a; kubectl delete pod nginx-2756690723-hllbp 结果显示这个pod 是删除了&#xff0c;但k8s很快自动创建新的pod,但是…

今日指数项目股票成交量对比功能

股票成交量对比功能 1. 股票成交量对比功能分析 1.1 模型示列 功能描述&#xff1a;统计A股大盘T日和T-1日成交量对比功能&#xff08;成交量为沪深两市成交量之和&#xff09; 1.2 接口示列 返回数据格式 服务路径&#xff1a;/api/quot/stock/tradeAmt 服务方法&#xf…

PCL uniform_sampling均匀采样抽稀

目录 一、概述二、代码三、结果 一、概述 均匀采样抽稀点云。 二、代码 uniform_sampling.cpp #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <pcl/keypoints/uniform_s…

[Admin] Things Need to Know

List View Bulk Actions Highlight: To take bulk actions on all of the available records in a list, you click the bulk action button without selecting any records.

利士策分享,攀登职场高峰:成功者的十大特质

利士策分享&#xff0c;攀登职场高峰&#xff1a;成功者的十大特质 在职场这个竞争激烈的舞台上&#xff0c;那些能够迅速崛起、实现职业辉煌的佼佼者&#xff0c;往往凭借一系列独特且鲜明的特质脱颖而出。以下是对这些特质的深入探讨&#xff1a; 第一章&#xff1a;高情商的…

怎么不用付费直接编辑pdf?5款pdf在线编辑器免费推荐给你!

在我们日常工作中&#xff0c;可能会经常需要直接编辑修改pdf内容&#xff0c;例如&#xff0c;在将文档发送给其它人之前&#xff0c;您可能需要进行一些修改&#xff1b;或者当扫描的文本出现错误时&#xff0c;您也需要进行修正。此时&#xff0c;如果有一款在线编辑器&…

黑神话悟空小西天

游戏里我们一开始就出现一个很可爱的小和尚&#xff0c;当脚步声传来&#xff0c;小和尚化身为一尊弥勒佛&#xff0c;而这尊弥勒佛的大小和位置都在说&#xff0c;这里没有弥勒佛的位置。 随后天命人进入一片雪地&#xff0c;遇到了赤尻马猴&#xff0c;打跑赤尻马猴&#xff…

从哪里下载高清解压视频素材?推荐五个优质素材资源网站

想制作吸引人的抖音小说推文&#xff0c;但不知道从哪里获取高清解压视频素材&#xff1f;今天就为大家推荐五个优秀的网站&#xff0c;帮助你轻松找到所需的素材&#xff0c;提升你的创作质量。 首先是蛙学网 作为国内顶级的短视频素材网站&#xff0c;蛙学网提供了丰富的4K高…

Transformer是不是BERT、GPT的妈?看完就知道了

Transformer变异衍生出来了两个超强悍的预训练模型 一、Transformer模型 Transformer是近年来深度学习领域中备受瞩目的模型之一&#xff0c;其核心思想是通过自注意力机制和位置编码来捕捉输入序列中的长距离依赖关系。 自注意力机制让模型在处理每个输入元素时能够关注到所有…

Proe 5.0资源百度网盘下载 附详细安装步骤

如大家所了解的&#xff0c;Proe又称作Pro/E&#xff0c;是比较常用的CAD/CAM/CAE软件之一&#xff0c;也是一款功能齐全的模具和产品设计三维的工具。 Proe在传统机械设计、家电设计以及模具设计方面&#xff0c;优势很突出。 首先&#xff0c;建模采用参数化设计&#xff0…