C++初阶C++入门

Posted Huang_ZhenSheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++初阶C++入门相关的知识,希望对你有一定的参考价值。

目录

命名空间

命名空间定义:

命名空间的使用: 

C++输入和输出

缺省参数

函数重载

函数重载的概念:

extern “C”:

引用

引用概念:

引用特性:

常引用:

引用和指针的区别:

使用场景:

1.做参数:

2.做返回值:


命名空间

首先,我们看下这段代码: 

#include<iostream>//头文件   
using namespace std;//命名空间
int main()
{
	cout << "hello world" << endl;//IO输出流
	return 0;
}

思考:使用using namespace std的作用是:?

这里暂且放一下,我们先来学习一下命名空间

命名空间定义:

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

C语言中

同一个作用域不能定义同名的

同时如果跟库的命名冲突了,C语言也无法解决 

#include<stdio.h>
int a = 0;
//int a = 0;
//同一个作用域不能定义同名的
int main()
{
	int a = 1;
	return 0;
}
//跟库的命名冲突了,C语言无法解决
#include<stdio.h>
#include<stdlib.h>//报错—>会发生冲突
int a = 0;
int rand = 10;
int main()
{
	int a = 1;
	printf("%d",rand);
	return 0;
}

因此,C++提出了命名空间来解决名字冲突的问题,于是namespace诞生了

#include<stdio.h>
#include<stdlib.h>
int a = 0;
namespace hzs//可以在命名空间里面定义变量
{
	int rand = 10;
}
int main()
{
	int a = 1;
	//printf("%d", rand);//访问全局的,打印出一个函数指针
	//如何处理?
	printf("%d",hzs::rand);//::域作用限定符,rand在左边bit的域里
	return 0;
}

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中 

除了定义变量,还有定义函数,定义类型,结构体,命名空间还可以嵌套定义

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

//除了命名空间,还可以定义函数
#include<stdio.h>
#include<stdlib.h>
namespace hzs//可以在命名空间里面定义变量
{
	//定义变量,定义函数,定义类型
	int rand = 10;
	int Add(int left, int right)
	{
		return left + right;
	}
	//还可以定义结构体
	struct Node
	{
		struct Node*next;
		int val;
	};
	//命名空间还可以嵌套定义
	namespace  hzs2
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}
//有了命名空间,他们互相之间就可以命名同名的东西
namespace cpp
{
	int rand = 100;
}
int main()
{
	int a = 1;
	printf("%d\\n", rand);//访问全局的,打印出一个函数指针
	//如何处理?
	printf("%d\\n",hzs::rand);//::域作用限定符,rand在左边bit的域里
	printf("%d\\n", cpp::rand);
	hzs::Add(1, 2);
	struct hzs::Node node;
	hzs::hzs2::Sub(1, 2);
	return 0;
}

命名空间的使用: 

 如果使用命名空间里面的东西呢?(具体的说明在注释里!)

//如何使用命名空间里面的东西?
//三种方式
//1,全部直接展开到全局
using namespace std;//std是包含C++标准库的命名空间,优点:用起来方便,缺点:把自己的定义暴露出去,导致命名污染
using namespace hzs;
int main()
{
	Add(1, 2);
	return 0;
}

//2,访问每个命名空间中的东西时,制定命名空间
//优点:不存在命名污染,缺点,用起来麻烦,每个都得去指定命名空间
std::rand


//3,把某个展开—>可以把常用的展开,不会造成大面积的污染
using hzs::Node;
using hzs::Add;
int main()
{
	struct Node n1;
	int ret = Add(1, 2);
	printf("%d",ret);
	hzs::rand = 100;
	return 0;
}

所以,这里就可以回到第一个问题的答案:

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

C++输入和输出

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

#include<iostream>
using namespace std;
int main()
{
	int n;
	cin >> n;//>>输入运算符/流提取运算符
	int* a = (int*)malloc(sizeof(int)*n);
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	for (int i = 0; i < n; i++)
	{
		cout << a[i]<<" ";//<<输出运算符/流插入运算符
	}
	return 0;
}

自动识别类型:使用C++输入输出更方便,不需增加数据格式控制,比如:整形--%d,字符--%c 

#include<iostream>
using namespace std;
int main()
{
	int n;
	cin >> n;//>>输入运算符/流提取运算符
	double* a = (double*)malloc(sizeof(double)*n);
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	for (int i = 0; i < n; i++)
	{
		cout << a[i] << " ";//<<输出运算符/流插入运算符
	}
	return 0;
}

#include<iostream>
using namespace std;
//自动识别类型

struct Student
{
	char name[10];
	int age;
};
int main()
{
	char ch = 'A';
	int i = 10;
	int* p = &i;
	double d = 1.11111;//最多输出五位

	//自动识别变量的类型
	cout << ch << endl;
	cout << i << endl;
	cout << p << endl;
	cout << d << endl;
	printf("%.2f\\n",d);

	//类似下面的场景使用printf更好用
	struct Student s = { "张三",18 };
	cout << "名字:" << s.name << " " << "年龄:" << s.age << endl;
	printf("名字:%s 年龄:%d\\n",s.name,s.age);
	return 0;
}

缺省参数

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

全缺省:

半缺省:

1. 半缺省参数必须从右往左依次来给出,不能间隔着给
2. 缺省参数不能在函数声明和定义中同时出现

函数重载

函数重载的概念:

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

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

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

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

C编译器,直接用函数名关联,函数名相同时,它无法区分

下面我们来看一下C的汇编代码:

不难看出,其Addi和Add的汇编代码一致 

 那么C++如何支持函数重载呢

函数名修饰规则——>不能直接用函数名,要对函数名进行修饰,带入参数特点修饰

函数名相同,只要参数不同,修饰出来名字就不同,那么就能区分,就支持重载

extern “C”:

有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译。比如:tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree两个接口来使用,但如果是C项目就没办法使用,那么他就使用extern “C”来解决。

引用

引用概念:

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

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

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
	//注意:这里跟C取地址用了一个符号&
	//之间无关联,各作各的用处
	int a = 10;
	int& b = a;
	int& c = a;
	int& d = b;
	return 0;
}

注意:引用类型必须和引用实体是同种类型的

引用特性:

1.引用在定义的时候必须初始化

//错误:int& e;

2.一个变量可以有多个引用 

3.引用一丹引用一个实体,再不能引用其他实体

//错误
int main()
{
    int a = 10;
	int& b = a;
	int c = 20;
	b = c;
}

常引用:

int main()
{
	//const引用
	const int a = 10;
	//int& b = a;//变成你的别名,还能修改你(不行)
	int c = 20;
	const int& d = c;//变成你的别名,不能修改你(行)
	return 0;
}

引用和指针的区别:

下面这段代码执行后,结果是什么样子的?

int main()
{
	int x = 0;
	int y = 1;
	int*p1 = &x;
	int*p2 = &y;
	int*&p3 = p1;
	*p3 = 10;
	p3 = p2;
	return 0;
}

通过调式可以发现, 一开始p1,p3存的是x的地址,经过P3=P2,把P2的地址给了P3,P1

使用场景:

1.做参数:

2.做返回值:

传值返回 :

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

调用Add函数的时候,1+2的结果给了C,return c好像就是把c作为返回值,拷贝给ret

实际上不是这样的,实际上return c不会用c去做这里的返回值,因为c出了作用域就不在了

—>编译器产生一个临时变量,临时变量去做返回值,传值返回(会有一次拷贝)

这个临时变量如果比较小,通常是寄存器,如果比较大,通常会在main函数中开一块临时空间 

证明一下上述说产生临时变量的说法:

如果将代码换成这样,会编译不通过 

int& ret = Add(1, 2);

如果换成这样,编译通过 (利用临时变量具有常性)

const int& ret = Add(1, 2);

传引用返回: 

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

 为什么这里的结果是 7 ???

我们先来看一段代码:

传引用返回:

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

传引用返回结果不确定,取决于平台销毁栈祯时是否会清理栈祯空间

如果清理了就是随机值,如果不清理,给ret的就是3

运行程序,可以发现VS上没有清理栈祯,我们进一步改动程序验证引用返回的危害性

小结:传值返回有拷贝,传引用返回没有拷贝,传引用返回有可能会带来数组越界访问,越界访问取到的值是不确定的,因为栈祯销毁了,清不清理是不知道的!

下面继续改动程序验证引用返回的危害:

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

用内存空间就像租房子一样,操作系统就是房东,我们申请内存,就是房东把房子给我们用,法律保护别人不会到你的房子里,释放内存就像是,我们退租了。房子还在,但是使用权不是我们的了,房东可能继续租房给别人了。

下面运行这段代码: 

#include<iostream>
using namespace std;
int& Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int& ret = Add(1, 2);
	//Add(3, 4);
	printf("hello\\n");
	cout << "Add(1,2) is:" << ret << endl;
	return 0;
}

输出结果:

 那什么情况下可以用引用返回呢?

出了func函数的作用域,ret变量会销毁,就不能用引用返回

出了func函数的作用域,ret变量不会销毁,就可以用引用返回

 总结:

A.引用返回的价值是减小拷贝

B.方便实现类似operator[]

以上是关于C++初阶C++入门的主要内容,如果未能解决你的问题,请参考以下文章

C++初阶---STL入门+(string)

C++初阶---C++基础入门(未写完)

C++从入门到入土第七篇:模板初阶

C++初阶:入门总结命名空间 | 缺省参数 | 函数重载 | 引用 | 内联函数

C++ 初阶 新语言入门介绍:命名空间,(全/半)缺省函数,函数重载,引用,内联,auto

C++ 初阶 新语言入门介绍:命名空间,(全/半)缺省函数,函数重载,引用,内联,auto