0
点赞
收藏
分享

微信扫一扫

基于单片机PM2.5监测系统仿真设计

柠檬果然酸 03-31 18:30 阅读 2

c++入门

C++入门知识主要是解决C语言的一些不足之处,我们下面直接步入正题。

我们下面来学习c++的第一个程序:
举例:`

#include <iostream>
//std是所有c++库命名空间
using namespace std;//放到全局

int main()
{
	cout << "hello world" << endl;
	return 0;
}

我们学习这个程序之前要先知道一个知识点:命名空间域

命名空间

我们在使用C语言的时候,通常会遇到变量定义冲突的情况,而C++中的命名空间域就是来解决这个问题的,下面我们来学习这个知识点:

域的概念

首先,域有以下几个分类:

其中全局域和局部域都会影响生命周期和访问,而命名空间域只影响访问。
而类域这里追秋还没有学到,所以这里就不给大家激讲解了。

下面我们写一个域的程序要让我们更快的了解域的概念:

namespace hhd1
{
	int x = 0;

}

namespace hhd2
{
	int x = 1;
}

int main()
{
	printf("%d\n", hhd1::x);
	printf("%d\n", hhd2::x);

	return 0;
}

其中namespace就是域的意思,hhd1和hhd2就是这个域的名字,我们在使用指定域中的变量的时候是用::符号来使用,::就是域作用限定符,作用是指定编译器去访问哪一个域。

接下来我们看下一个程序:

namespace hhd1
{
	int x = 111;
}

using namespace hhd1;

int main()
{
	printf("%d\n", x);

	return 0;
}

讲解:using namespace hhd1 就是将命名空间域hhd1展开,使编译器可以访问到该空间中,

我们在命名空间域中还可以定义一些常见的量,比如:函数,结构体。

namespace hhd1
{
	int x = 0;

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

	struct Node
	{
		struct Node* next;
		int val;
	};
}

int main()
{
	printf("%d\n",hhd1::x);
	
	//定义结构体指针
	struct hhd1::Node phead;
	
	return 0;
}

其中我们看到函数、结构体也是可以在命名空间域中定义的,其中在主函数中还定义了结构体指针。

命名空间域的嵌套

举例:

#include <iostream>

namespace hhd
{
	namespace sz
	{
		int x = 111;
	}

	namespace ls
	{
		int x = 120;
	}
}

int main()
{
	printf("%d\n", hhd::sz::x);
	printf("%d\n", hhd::ls::x);
	return 0;
}

我们在使用命名空间域的时候可以对域进行嵌套定义,这样就可以防止在同一域中的变量定义冲突的问题,当然,这个嵌套是可以多次嵌套的,只要你能把这个逻辑理清楚的话。

域的使用方式

1.不展开,在指定域搜索

#include <iostream>
namespace hhd
{
	int x = 120;
}
int main()
{
	printf("%d\n", hhd::x);
	return 0;
}

2.使用using将命名空间中某个成员引入

using N::b;
int main()
{
	printf("%d\n", N::a);
	printf("%d\n", b);
	return 0;
}

3.使用using namespace 命名空间名称 引入

using namespce N;
int main()
{
	printf("%d\n", N::a);
	printf("%d\n", b);
	Add(10, 20);
	return 0;
}

C++输入输出

输出cout

使用方法:

int main()
{
	cout << "xxxxx" << endl;
	return 0;
}
int main()
{
	int x = 0;
	double y = 1.2;
	cout << x<< y << endl;

	return 0;
}

在对cout语句使用的时候不需要像printf中一样注明类型,就可以直接打印在显示器上。

输入cin

举例:

int main()
{
	int x = 0;
	double y = 1.2;
	cin >> x >> y ;
	cout << x << y << endl;
	return 0;
}

缺省参数

举例:

#include <iostream>

using namespace std;

void func(int i = 0)
{
	cout << i << endl;
}
int main()
{
	func(1);
	func();
	return 0;
}

运行结果:
在这里插入图片描述

缺省参数有以下几个分类:

全缺省

void func(int x = 10, int y = 20, int z = 30)
{
	cout << "x="<< x << endl;
	cout << "y="<< y << endl;
	cout << "z="<< z << endl<<endl;
}

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

运行结果:
在这里插入图片描述

半缺省(部分缺省)

void func(int x, int y = 20, int z = 30)
{
	cout << "x="<< x << endl;
	cout << "y="<< y << endl;
	cout << "z="<< z << endl<<endl;
}

int main()
{
	func(1, 2, 3);
	func(1, 2);
	func(1);

	return 0;
}

运行结果:
在这里插入图片描述

函数重载

函数重载概念

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

函数重载有以下几个分类:

参数类型不同
// 1、参数类型不同
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}

函数名相同,但是参数的类型不同。

参数个数不同
// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}

函数名相同,但是函数的参数数量不同。

参数顺序不同
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

函数名相同,函数的参数顺序不同,这一个不同的点影响着编译器中对于函数搜索规则的变化,这个是C语言所没有的。这个点之后的文章追秋会重点讲解!

引用

一个变量可以有多个别名

举例:

int main()
{
	int a = 0;
	//引用,b就是a的别名
	int& b = a;

	cout << &b << endl;
	cout << &a << endl;

	//可以取多次别名
	int& c = a;
	//也可以对别名取别名
	int& d = b;

	cout << &c << endl;
	cout << &d << endl;

	return 0;
}

运行结果:
在这里插入图片描述

下面来举一个例子来引出引用在函数方面的使用:

void Swap(int& c, int& d)
{
	int tmp = c;
	c = d;
	d = tmp;
}

int main()
{
	int a = 1;
	int b = 5;

	Swap(a, b);
	cout << a << endl << b << endl;
	return 0;
}

运行结果:
在这里插入图片描述
此时ab中的值已经被交换,那么这个时候就有人要问了,在函数形参取名的时候可以和实参一样吗?
解答:可以!
在这里插入图片描述

引用在定义时必须初始化

int main()
{
	int a = 0;
	int& b = a;

	int& c;
	return 0;
}

在这里插入图片描述
我们可以看到上述代码在编译的时候报错。
为什么引用必须初始化?
引用如果不初始化的话,那么回合赋值产生歧义。

int main()
{
	int a = 0;
	int b = 10;

	int d;
	int& c;
	c = b;
	d = b;
	return 0;
}

上述代码c的引用和d的赋值部分根本分不清楚。

引用一旦引用一个实体,再不能引用其他实体

引用一旦确定引用一个实体之后,就不能在引用另一个实体了。

int main()
{
	int a = 0;
	int b = 10;

	int& c = a;
	&c = b;
	return 0;
}

下面说几个引用在函数传参的应用:

void PushBack(struct Node*& phead, int x)
{
	phead = newnode;
}
int main()
{
	struct Node* plist = NULL;

	return 0;
}

此处就是函数中的phead就是struct Node* plist的别名,也就是phead等价于plist;

使用场景

参数
void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
返回值

场景一:

int func()
{
	int a = 1;
	return a;
}
int main()
{
	int ret = func();
	cout << ret << endl;
	return 0;
}

在这里插入图片描述
场景二:

int& func()
{
	int a = 1;
	return a;
}
int main()
{
	int ret = func();
	cout << ret << endl;

	return 0;
}

在这里插入图片描述
场景三:

int& func()
{
	int a = 1;
	return a;
}
int main()
{
	int& ret = func();
	cout << ret << endl;

	return 0;
}

在这里插入图片描述
此时在这个场景下做进一步的改动:

int& func()
{
	int a = 1;
	return a;
}
int& fun()
{
	int b = 6;
	return b;
}
int main()
{
	int& ret = func();
	cout << ret << endl;

	fun();
	cout << ret << endl;

	return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述
在以上这种场景中,只是简单调用了另一个函数,其实啥都没做,这里就会出现随机值。

指针和引用的区别

内联函数

#define ADD(a,b) ((a) + (b)) 

int main()
{
	int a = 1;
	int b = 2;

	int c = ADD(a, b);
	cout << c << endl;
	return 0;
}

以上代码就是宏的使用的一个简单代码,其中也会出现几个问题:为什么a、b要单独加括号将他们单独放在一起:
解答:因为可能原始参数可能传的不仅仅是一个参数,而是一个表达式,下面我们来看:

#define ADD(a,b) ((a) + (b)) 

int main()
{
	int a = 1;
	int b = 2;

	int c = ADD(a + b , a - b);
	cout << c << endl;
	return 0;
}

上面这个例子就很好的说明了宏的一个正确使用方式。相比较于函数,宏是不需要建立栈帧的,也就减少了栈帧空间的开销,当然宏也不是完美的,宏也有缺点:

而内联函数就将函数和宏的优势结合在了一起:
我们知道:函数在传递参数的时候,如果参数是表达式,那么会将表达式的值算好在传值,而宏是不需要单独建立栈帧,编译器在编译的时候会自动将宏的内容进行替换,这里内联函数就将这一点完美的应用到了:

inline int ADD(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 1;
	int b = 2;

	int c = ADD(a + b , a - b);
	cout << c << endl;
	return 0;
}

这里的运算不建立栈帧,直接在原地进行替换且参数是运算好的。

内联函数特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
  3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。下面将一个例子来说明这个问题:

下面将一个内联函数一个在实践当中的一个小应用的地方,
当一个函数的声明和定义没有分离的时候,函数在运行的时候会出现报错:
在这里插入图片描述
想要解决这个问题我们有以下几个解决方法:

auto关键字

识别类型

int main()
{
	int aw = Add(1, 2);

	int a = 1;

	auto b = a;
	auto c = &a;
	auto* d = &a;
	
	return 0;
}

在上述的情况下,定义一个新的数据就不用特意声明他的类型,系统会自动识别数据类型并完成定义。

auto关键字也可以定义函数类型:
在这里插入图片描述
当然在这里自动识别类型定义显得价值不大,但是在以后类型很复杂的情况下,auto函数的价值就会显示出来。

auto不能推导的场景

不能作函数的参数

在这里插入图片描述
编译器允许auto作为返回类型但不能作为参数。
在这里插入图片描述

不能用来声明数组类型
void TestAuto()
{
	int a[] = {1,2,3};
	auto b[] = {456};
}

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

范围for的语法

int main()
{
	int a[] = { 1,2,3,4,5 };
	for (auto e : a)
	{
		cout << e << endl;
	}
	return 0;
}

这其实就是一种新的for循环的使用方式,auto会将数组a中的值依次放到e中,然后我们打印的结果就是:
在这里插入图片描述

范围for的使用条件

指针空值nullptr(C++11)

这个点其实是在弥补C++之前的缺陷:

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
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。

举报

相关推荐

0 条评论