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.mzph.cn/news/880560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机械键盘驱动调光DIY--【DAREU】

1 下载键盘对应的驱动&#xff0c;不要装到C盘 达尔优驱动下载中心 2 驱动更改教程 标准模式 键盘功能 鼠标功能 切换灯光 切换配置文件 多媒体 windows快捷键 禁用 Fn 启动程序 文本功能 光标定位 FN模式 灯光效果设置 注意 宏--自定义功能

面试扩展知识点

1.C语言中分为下面几个存储区 栈(stack): 由编译器自动分配释放堆(heap): 一般由程序员分配释放&#xff0c;若程序员不释放&#xff0c;程序结束时可能由OS回收全局区(静态区): 全局变量和静态变量的存储是放在一块的&#xff0c;初始化的全局变量和静态变量在一块区域&#…

用于探索和测试API的开源IDE工具-Bruno

1、前言 在进行软件开发与测试过程中&#xff0c;无论是开发人员还是测试人员&#xff0c;都会或多或少地进行接口调试与接口测试。尤其针对那种测试流程规范性很高的项目&#xff0c;测试人员进行接口测试是不可或缺的一部分。而这其中&#xff0c;大多数都会使用 Postman 作…

9.26 Buu俩题解

[CISCN2019 华东北赛区]Web2 看wp写完之后写的 知识点 存储型XSS与过滤绕过sql注入 题解 好几个页面&#xff0c;存在登录框可以注册&#xff0c;存在管理员页面(admin.php) ->既然存在管理员页面&#xff0c;且直接访问admin.php提示我们 说明存在身份验证&#xff0…

crypto-js解密报错malformed utf-8 data

在进行加解密处理时出现这个问题。 但是当在一个完整程序运行环境内加密字符串&#xff0c;解密字符串是没问题的。 当把加密的字符存储到txt文件&#xff0c;在读取解密时出现错误无法解密。 最后&#xff0c;使用res.replace(/\s/g,‘’)正则过滤掉txt文件内的空格就成功了。…

若依生成主子表

一、准备工作 确保你已经部署了若依框架&#xff0c;并且熟悉基本的开发环境配置。同时&#xff0c;理解数据库表结构对于生成代码至关重要。 主子表代码结构如下&#xff08;字表中要有一个对应主表ID的字段作为外键&#xff0c;如下图的customer_id&#xff09; -- ------…

SOCKS5代理和HTTP代理哪个快?深度解析两者的速度差异

在现代互联网环境中&#xff0c;使用代理IP已经成为了许多人日常生活和工作的必备工具。无论是为了保护隐私&#xff0c;还是为了访问某些特定资源&#xff0c;代理IP都扮演着重要的角色。今天&#xff0c;我们就来聊聊SOCKS5代理和HTTP代理&#xff0c;看看这两者到底哪个更快…

【赵渝强老师】K8s中的Deployment控制器

K8s的Deployment将Pod部署成无状态的应用程序&#xff0c;它只关心Pod的数量、Pod更新方式、使用的镜像和资源限制等。由于是无状态的管理方式&#xff0c;因此Deployment中没有角色和顺序的概念&#xff0c;换句话说&#xff1a;Deployment中没有状态。   通过使用Deploymen…

UART 如何实现波特率自动检测原理介绍

为什么需要波特率自动检测机制 我们都知道&#xff0c;串口通讯是需要约定波特率才能够进行准确的通讯。此时假设&#xff0c;你们公司开发了一个板子&#xff0c;有一个串口与外接设备进行通讯。因为你们公司开发的产品要提供给多个客户&#xff0c;而有些客户可能之前就有一…

MacOS Catalina 从源码构建Qt6.2开发库之02: 配置QtCreator

安装Qt-creator-5.0.2 在option命令中配置Qt Versions指向 /usr/local/bin/qmake6 Kits选入CLang

SSM+Vue社区物业管理系统

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

【cache】浅析四种常用的缓存淘汰算法 FIFO/LRU/LFU/W-TinyLFU

本文浅析淘汰策略与工作中结合使用、选取&#xff0c;并非针对算法本身如何实现的 文章目录 FIFOLFULRUW-TinyLFU实践与优化监控与调整 FIFO first input first output &#xff0c; 先进先出&#xff0c;即最早存入的元素最先取出&#xff0c; 典型数据结构代表&#xff1a;…

Elasticsearch使用Easy-Es + RestHighLevelClient实现深度分页跳页

注意&#xff01;&#xff01;&#xff01;博主只在测试环境试了一下&#xff0c;没有发到生产环境跑。因为代码还没写完客户说不用弄了( •̩̩̩̩&#xff3f;•̩̩̩̩ ) 也好&#xff0c;少个功能少点BUG 使用from size的时候发现存在max_result_window10000的限制&…

TypeScript介绍和安装

TypeScript介绍 TypeScript是由微软开发的一种编程语言&#xff0c;它在JavaScript的基础上增加了静态类型检查。静态类型允许开发者在编写代码时指定变量和函数的类型&#xff0c;这样可以在编译时捕获潜在的错误&#xff0c;而不是等到运行时才发现问题。比如&#xff0c;你…

基于STM32的无人驾驶车辆系统

目录 引言项目背景环境准备 硬件准备软件安装与配置系统设计 系统架构关键技术代码示例 传感器数据采集与处理路径规划与避障控制实时反馈与控制系统应用场景结论 1. 引言 随着无人驾驶技术的发展&#xff0c;嵌入式系统在无人驾驶车辆中的应用变得越来越重要。STM32作为高效…

Java SPI 原理、样例

在 Java 中&#xff0c;SPI&#xff08;Service Provider Interface&#xff09;全称为服务提供者接口&#xff0c;它是一种用于实现框架扩展和插件化的机制。 一、SPI 作用 允许在运行时动态地为接口查找服务实现&#xff0c;而不需要在代码中显式地指定具体的实现类。 这使得…

WordPress精选文章如何添加侧边栏和页面?

WordPress精选帖子是一项功能&#xff0c;可让用户在其网站主页或其他值得注意的部分突出显示特定帖子。这些精选帖子通常以视觉上独特的方式显示&#xff0c;例如以滑块、网格或轮播格式显示&#xff0c;以提高其可见性和对访问者的吸引力。 网站所有者可以手动选择他们想要推…

云计算Openstack

OpenStack是一个开源的云计算管理平台项目&#xff0c;由美国国家航空航天局&#xff08;NASA&#xff09;和Rackspace公司合作研发并发起&#xff0c;以Apache许可证授权。该项目旨在为公共及私有云的建设与管理提供软件支持&#xff0c;通过一系列相互协作的组件实现云计算服…

MMD模型及动作一键完美导入UE5-衣服布料模拟(四)

1、给角色刷布料 1、打开角色,通过Window->Clothing打开模型布料窗口 2、选中裙子右键,创建布料数据 3、选择裙子,右键->应用布料数据 4、激活布料画笔,就可以开始绘制布料了 5、调整画笔大小和布料值进行绘制,布料值为0表示刚体

高校教师成果管理小程序的设计与实现springboot(lw+演示+源码+运行)

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对高校教师成果信息管理混乱&#xff0c;出错率高&#xff0c;信息安全…