C++基础语法(C++入门篇一)

一、命名空间

在一个C/C++项目中,变量、函数和类在项目中都是大量存在的,这些变量、函数和类的名称若都是存于全局变量中,可能会导致名称冲突。所以命名空间作用就是对标识符的名称本地化,避免名字冲突和污染——所以有了关键字namespace

1. :: 域作用限定符

先介绍一下这个 :: 符号。
:::域作用限定符

eg1: 使用局部域和全局域举例

#include <stdio.h>

int a = 0;      //全局域

int main()
{
	int a = 1;  //局部域
	printf("%d\n", a);    //访问局部域

	//::(域作用限定符)
	printf("%d\n", ::a);  //访问全局域
	return 0;
}

//output: 1 0

eg2:在上一个例子的基础上在加上命名空间域举例

namespace kpl
{
	int a = 0;  //命名空间域
}

int a = 1;      //全局域

int main()
{
	int a = 2;  //局部域
	printf("%d\n", a);       //访问局部域

	printf("%d\n", ::a);     //访问全局域

	printf("%d\n", kpl::a);  //指定访问命名空间域
	return 0;
}

code_result:
::域作用限定符介绍

通常在访问的时候局部域优先,但是加上 :: 就可以访问全局域。在 :: 域作用限定符之前后分别加上命名空间域的名字和要访问的变量、函数、类型,就可以访问命名空间域内部的数据

2. 作用

命名空间:防止命名冲突或名字污染,对标识符的名称进行本地化

命名冲突:

  1. 我们的项目和库中的名字冲突。
  2. 互相冲突,因为一个项目可能需要多个人写,因此可能会出现同样的名字。

eg:我们和库中的名字冲突

#include <stdio.h>
#include <stdlib.h>

int rand = 10;

int main()
{
	printf("%d\n", rand);
	return 0;
}

eg_result
在这里插入图片描述

rand重定义,以前库中的定义是函数。因为C语言解决不了这样的问题,所以在C++中提出了namespace。

3. 命名空间的定义

①简单使用

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

//kpl是命名空间的名字,命名空间的名称可以随便定义,一般开发中都是用项目名字作为命名空间名
namespace kpl
{
	//变量
	int rand = 10;

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

	//类型
	struct Node
	{
		struct Node* next;
		int val;
	};
}

②命名空间嵌套

eg:
代码运行结果分析

kpl2是命名空间kpl1中的域。所以再使用的时候要从外向里包含

4. 命名空间的使用

  1. 加命名空间名称及作用域限定符
namespace kpl
{
	int a = 0;
	int b = 1;

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

int main()
{
	printf("1 + 2 = %d, a = %d\n", kpl::Add(1, 2), kpl::a);
	return 0;
}

//output:1 + 2 = 3, a = 0
  1. 使用using将命名空间中某个成员引入(展开部分成员)
    建议:常用的库对象、类型等方面使用
namespace kpl
{
	int a = 0;
	int b = 1;

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


using kpl::b;
int main()
{
	//这里就不需要加::域访问限定符,可以直接访问b
	printf("%d\n", b);
	return 0;
}

//output:1
  1. 使用using namespace命名空间名称引入
    建议:不要用这种方式,如果使用这种方式,命名空间就没有存在的意义了。
namespace kpl
{
	int a = 0;
	int b = 1;

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

using namespace kpl;
int main()
{
	//对命名空间展开,就等于命名空间的所有内容就展开到全局域中,可以和全局域一样使用。
	//对命名空间展开,也意味着该命名空间没做到它该有的作用
	int c = Add(10, 20);
	printf("%d\n", a);
	printf("%d\n", c);
	return 0;
}

5. 小结

  1. 如果展开命名空间域之后,所展开命名空间中的变量和全局变量命名一致会报错。因为命名空间域展开后,其中的变量本质就是全局域的变量,而同一域不能有相同的名称。
  2. 一个命名空间就定义了一个新的作用域。不展开的情况下,命名空间的所有内容都局限在该命名空间中。
  3. 同一个工程允许存在多个相同名称的命名空间,编译器最后会合成为一个命名空间。
  4. 域的分类
    • 类域(和class有关)
    • 命名空间域
    • 作用域
      a. 全局域
      b.局部域

二、C++输入输出

1. 简单使用

注:都包含在<iostream>这个头文件中。cout和cin是全局的流对象,endl是C++特殊符号。这些也都包在一个命名空间std(后面讲)中

  1. cout(标准输出对象) —— 控制台
  2. cin(标准输入对象) —— 键盘
  3. endl(换行输出)

举例介绍
eg:

#include <iostream>  //包含C++输入输出头文件

//全部展开命名空间
using namespace std;

int main()
{
	int a;
	double b;
	char c;

	//自动识别类型
	cin >> a;
	cin >> b >> c;
	cout << a << endl;
	cout << b << " " << c << endl;
	return 0;
}

2. std介绍

std是C++标准库的命名空间,C++标准库的定义和实现都放到这个命名空间当中

eg:

#include <iostream>

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

注:为了区分C中的头文件,使用C++头文件不带.h。同时因为C++要兼容C所以在C++程序中使用C的头文件去掉后缀.h在头文件前+c。eg:#include <cstdio>

三、缺省参数

1. 概念

缺省参数:声明或定义函数时为函数的参数指定一个缺省值,在调用的时候,如果没有传参则就使用缺省值。如果传参了就是用传的这个参数。

eg1:

#include <iostream>
using namespace std;

//给参数a一个缺省值
void Func(int a = 0)
{
	cout << a << endl;
}

int main()
{
	//同时调用Func函数一个传参一个不传参
	Func();        //没有传参     使用参数的默认值
	Func(10);      //传参        使用指定的实参
	return 0;
}

//output:0 10

eg2:
缺省参数
注:

  • 当声明和定义分离时,声明给缺省参数定义可以不给缺省参数(在编译的时候,如果是缺省参数,会转换成参数列表)。如果声明不给定义给,在编译阶段,编译不过去
  • Func()调用是直接无参调用,使用缺省参数就会转换成参数列表中的参数

2. 分类

①全缺省参数

//全缺省参数
void Func(int a= 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;          //显示a的值
	cout << "b = " << b << endl;          //显示b的值
	cout << "c = " << c << endl << endl;  //显示c的值
}

int main()
{
	Func();          //a、b、c全使用缺省参数

	//只传一部分参数,从左向右给
	Func(1);         //a=1    b、c使用缺省参数
	Func(1,2);       //a=1 b=2 c使用缺省参数
	Func(1,2,3);     //a=1 b=2 c=3
	return 0;
}

代码运行结果:
代码运行结果
注: 传参时只传一部分参数时,从左向右给

②半缺省参数

//半缺省参数
void Func(int a, int b = 10, int c = 20)   //半缺省参数从右向左给
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}

int main()
{
	//因为是半缺省参数,a没有给缺省参数,所以至少要传一个实参
	//Func();       //error  
	
	//只传一部分参数,从左向右给
	Func(1);         //a=1            b、c使用缺省参数
	Func(1, 2);       //a=1 b=2       c使用缺省参数
	Func(1, 2, 3);     //a=1 b=2 c=3

	return 0;
}

代码运行结果:
代码运行结果

3. 应用

在一些初始化的时候会使用

eg:stack初始化要赋容量初始值capacity
因为扩容等一些操作有代价(而且不一定原地扩容),不熟悉栈的可以看一下这篇C实现的栈

所以我们在对栈初始化的时候可以像下面这样

void StackInit(ST* ps, int capacity = 4) //默认初始化4个大小
{}

int main()
{
	//假设我们事先知道要插入100个数据,那我们就开100个数据的空间,
	//所以我们可以这样传参
	StackInit(&st1, 100);

	//当然如果事先不知道要开辟多少空间,可以不传参
	StackInit(&st2);
	return 0;
}

4. 小结

  1. 缺省值必须是常量或者全局变量
  2. 半缺省参数必须从右向左依次给,不能间隔
  3. 缺省参数不能在函数的声明和定义中同时出现
    原因:如果声明和定义同时给缺省值,并且两个位置提供的缺省值不同,那编译器无法确定使用那个缺省值。
  4. 函数声明不给缺省参数,函数定义给缺省参数,在编译阶段,编译不过去
    • 原因:可能会导致调用歧义。如果传一个参数,并且函数定义有缺省参数,因为是声明和定义分离,所以分离编译,在链接阶段会出现调用歧义
    • 为了解决上述问题,所以当声明和定义分离时,声明给缺省值,定义不给。声明给的缺省值在编译阶段会转换成参数列表。

四、函数重载

1. 概念

C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 类型 类型顺序)不同,常用来处理功能相似,类型不同的数据。

2. 三种重载方式

注:返回值不同,不构成重载。从后面讲的名字修饰原理也可以理解

eg: 直接报错

#include <iostream>
using namespace std;

//参数类型顺序不同
int f(int a)
{
	cout << "int f(int a)" << endl;
}

void f(int a)
{
	cout << "void f(int a)" << endl;
}

int main()
{
	f(1);
	return 0;
}

①参数类型不同

#include <iostream>
using namespace std;

//参数类型不同
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;
}

double Add(int left, double right)
{
	cout << "double Add(int left, double right)" << endl;

	return left + right;
}

int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(1.1, 2.2) << endl;
	cout << Add(1, 2.2) << endl;
	return 0;
}

//运行结果:
//int Add(int left, int right) :3
//double Add(double left, double right) :3.3
//double Add(int left, double right) :3.2

②参数个数不同

eg1:

#include <iostream>
using namespace std;

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

void f(int a)
{
	cout << "f(int a)" << endl;
}

int main()
{
	f();
	f(1);
	return 0;
}

//运行结果:
//f()
//f(int a)

eg2:

#include <iostream>
using namespace std;

//构成重载但是函数调用不明确,存在歧义
void f()
{
	cout << "f()" << endl;
}

void f(int a = 0)
{
	cout << "f(int a)" << endl;
}

int main()
{
	f(); //对重载函数的调用不明确

	return 0;
}

③参数类型顺序不同

本质还是参数类型不同

#include <iostream>
using namespace std;

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

void f(char a, int b)
{
	cout << "void f(char a, int b)" << endl;
}

int main()
{
	f(10, 'a');
	f('a', 10);
	return 0;
}

//运行结果:
//void f(int a, char b)
//void f(char a, int b)

3. 函数重载的原理

C++支持函数重载的原理 – 原因在于名字修饰

  1. 这里涉及程序运行与预处理的相关知识。当函数的声明和定义在不同的文件,我们要调用该函数时,函数声明所在的文件是没有函数的地址的,只是一个声明,但是要找到这个函数怎么办呢?所以这就是链接阶段处理的问题,函数声明所在文件没有函数的地址,则会去函数定义所在文件的符号表找该函数的地址,然后链接在一起。(这是程序运行与预处理的相关知识)
  2. 另一个问题,C++支持函数重载,当有好几个函数名相同的函数时,怎么在链接时寻找到正确的函数?
    解决问题的方法: 名字修饰(这里介绍Linux中g++编译器的命名规则。gcc是编译C语言写的程序,g++是编译C++写的程序,当然C++包含C所以g++也可以编译C程序,不建议这样)

g++函数修饰后的名字变成: _Z + 函数长度 + 函数名 + 类型首字母
eg:

void ffffff()
{
	cout << "f()" << endl;
}

void ffffff(int a)
{
	cout << "f(int a)" << endl;
}

修饰后:所以这里就能看出C++如何支持的函数重载
修饰后

所以在名字修饰规则里,没有返回值因素的存在,所以返回值类型不构成重载,编译器没办法区分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kpl_20

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值