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

news2024/9/28 16:23:40

这篇文章主要对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 once

namespace 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.h
  void Func(int a = 10);
  
  // a.cpp
  void 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.coloradmin.cn/o/2174066.html

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

相关文章

遍历递归数结构,修改里的disabled值

返回参数中新增字段 disabled,后端给的值为1和2, disabled1时&#xff0c;代表该节点需要置灰&#xff0c;不可选中 现在需要将disabled的值,改为布尔类型; 后端给的数结构是对象类型,tree接收数组类型; 先将对象类型的数据,遍历递归,修改里面的disabled值,最后再加[ ],改为…

Magnific推V2图像生成服务 可直出4K图像

人工智能 - Ai工具集 - 集合全球ai人工智能软件的工具箱网站 近日&#xff0c;AI图像处理领域再迎重大突破&#xff0c;Magnific推出的V2图像生成服务引领行业潮流。此次升级&#xff0c;不仅使Magnific从高端软件跻身为顶级AI图像生成器&#xff0c;更彰显了其在技术创新及用…

html+css+js实现Progress 进度条

实现效果 代码实现 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>progress</title><st…

动手学深度学习(李沐)PyTorch 第 4 章 多层感知机

4.1 多层感知机 隐藏层 我们在 3.1.1.1节中描述了仿射变换&#xff0c; 它是一种带有偏置项的线性变换。 首先&#xff0c;回想一下如 图3.4.1中所示的softmax回归的模型架构。 该模型通过单个仿射变换将我们的输入直接映射到输出&#xff0c;然后进行softmax操作。 如果我们…

5V继电器模块详解(STM32)

目录 一、介绍 二、模块原理 1.原理图 2.引脚描述 3.工作原理介绍 三、程序设计 main.c文件 relay.h文件 relay.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 继电器(Relay)&#xff0c;也称电驿&#xff0c;是一种电子控制器件&#xff0c;它具有控制系统…

力扣75道热门算法题 第一天

1768、交替合并字符串 题解&#xff1a; class Solution {public String mergeAlternately(String word1, String word2) {int m word1.length(),n word2.length();int i 0, j 0; //双指针遍历两个数组StringBuilder sb new StringBuilder(); //生成一个StringBuilder对象…

如何禁止电脑上某个软件运行?电脑设置禁止运行软件的4个方法速成

在日常使用电脑的过程中&#xff0c;可能会遇到需要禁止某些软件运行的情况。 无论是为了防止员工随意使用与工作无关的软件&#xff0c;还是为了管理孩子的电脑使用时间&#xff0c;禁止特定软件运行都是有效的解决方案。 下面介绍4个速成方法&#xff0c;帮你轻松禁止电脑上…

AlmaLinux 9 安装mysql8.0.38

文件下载 https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.39-linux-glibc2.12-x86_64.tar 选择合适系统版本 下载后解压 tar -xvf mysql-8.0.39-linux-glibc2.12-x86_64.tar解压后里面有三个文件夹 使用mysql-8.0.39-linux-glibc2.12-x86_64.tar.xz即可&#xff0c…

《牧神记》PV初体验,玄机科技再塑经典国漫

2024年9月26日《牧神记》动画版在Bilibili正式宣布定档于10月27日&#xff0c;并在各大社交媒体平台上发布系列精美海报&#xff0c;瞬间点燃了广大动漫爱好者的热情与期待&#xff01;作为一个长期关注国产动画的漫迷&#xff0c;我对玄机科技的作品一直抱有很高的期待&#x…

Ubuntu 16.04安装填坑记录

一. 问题描述&#xff1a; &#xff08;1&#xff09;Ubuntu 16.04使用USB启动盘安装时&#xff0c;出现"try ubuntu without installation"或“install ubuntu”选择&#xff0c;Enter选择安装后&#xff0c;显示器黑屏无任何显示。 原因分析&#xff1a; 显示黑…

26:WDG看门狗

WDG看门狗 1、看门狗的简介2、独立看门狗3、窗口看门狗4、独立看门狗代码5、窗口看门狗代码 1、看门狗的简介 1、看门狗的作用是监控程序的运行状态。当程序因为设计漏洞、硬件故障、电磁干扰等原因&#xff0c;出现卡死或跑飞现象时&#xff0c;看门狗能及时复位程序&#xf…

现行经济下,兜底的生存方式是什么?

最近在网上看到一个问题&#xff0c;在经济下行的时候&#xff0c;兜底的生存方式是什么,看到之后&#xff0c;就随便写写。 在现在的经济环境下&#xff0c;看看网上的裁员、降薪信息 到底什么才是兜底的生存方式&#xff0c;原来认识的人&#xff0c;包括我自己也在想失业后…

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

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

《深度学习》卷积神经网络 数据增强、保存最优模型 原理解析及实例实现

目录 一、数据增强 1、什么是数据增强 2、目的 3、常用的数据增强方法 4、数据预处理 用法&#xff1a; 5、使用数据增强增加训练数据 二、保存最优模型 1、什么是保存最优模型 2、定义CNN模型 运行结果&#xff1a; 3、设置训练模式 4、设置测试模式、保存最优模…

影院管理新篇章:小徐的Spring Boot应用

第三章 系统分析 整个系统的功能模块主要是对各个项目元素组合、分解和更换做出对应的单元&#xff0c;最后在根据各个系统模块来做出一个简单的原则&#xff0c;系统的整体设计是根据用户的需求来进行设计的。为了更好的服务于用户要从小徐影城管理系统的设计与实现方面上做出…

羽毛类型检测系统源码分享

羽毛类型检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

智尚招聘求职小程序V1.0.17

微信小程序招聘管理系统。支持多城市、人才版块、招聘会、职场资讯、经纪人入驻等功能。提供全部无加密源码&#xff0c;支持私有化部署。 V1.0.17增加功能 1、增加求职者投简历时检测简历状态功能 更新后无需重新发布前端

16 Midjourney从零到商用·实战篇:产品工业设计

未来的产品设计师&#xff0c;一定真的不能只会画图&#xff0c;要往多元化发展&#xff0c;要有更强的思维能力综合的判断能力&#xff0c;懂市场&#xff0c;懂加工&#xff0c;懂生产等&#xff0c;变成综合类人才。基础的设计工作部分已经被AI所取代&#xff0c;生产力在一…

redis 中IO多路复用与Epoll函数

一 IO多路复用 1.1 IO多路复用作用 1.2 同步异步阻塞非阻塞

UE4_Niagara基础实例—使用自定义模块二

效果&#xff1a; 粒子限制在球形范围内 操作步骤&#xff1a; 1、因为我们要对粒子的位置进行控制&#xff0c;所以需要开启本地空间&#xff0c;粒子与发射器位置有关。使用GPU计算模拟&#xff0c;需勾选固定边界。 2、简单调节其它参数&#xff1a; 3、现在我想把粒子锁定…