前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++修行之道】命名空间 、C++输入&输出、缺省参数和函数重载

【C++修行之道】命名空间 、C++输入&输出、缺省参数和函数重载

作者头像
走在努力路上的自己
发布2024-05-26 08:20:36
470
发布2024-05-26 08:20:36
举报

一、C++关键字(C++98)

C++总计63个关键字,C语言32个关键字 ps:下面我们只是看一下C++有多少关键字,不对关键字进行具体的讲解。后面我们学到以后再 细讲。

关键字1

描述1

关键字2

描述2

asm

汇编代码块

auto

自动类型推导

do

do-while循环开始

double

双精度浮点数类型

if

条件语句

inline

内联函数修饰符

return

返回语句

short

短整型

try

异常处理块开始

typedef

类型定义

continue

跳过当前循环的剩余部分,进入下一次循环

for

for循环

bool

布尔类型

dynamic_cast

动态类型转换

int

整型

signed

有符号类型修饰符

typeid

运行时类型信息操作符

long

长整型

public

访问修饰符(公有)

sizeof

获取数据类型或对象的大小

break

跳出循环或switch语句

typename

类型名,用于模板中

else

if语句的否定分支

throw

抛出异常

case

switch语句的分支标签

catch

异常处理块结束

enum

枚举类型定义

explicit

禁止类构造函数的隐式转换

mutable

可变数据成员修饰符

namespace

命名空间定义

static

静态成员修饰符

static_cast

静态类型转换

union

联合类型定义

unsigned

无符号类型修饰符

wchar_t

宽字符类型

default

switch语句的默认分支标签

char

字符类型

export

导出类定义(已弃用)

new

动态内存分配操作符

struct

结构体类型定义

using

引用命名空间或类型别名

friend

友元声明

class

类定义

extern

外部变量或函数声明

operator

操作符重载

switch

switch语句

virtual

虚函数修饰符

register

寄存器变量修饰符(已弃用)

const

常量修饰符

false

布尔值false

private

访问修饰符(私有)

true

布尔值true

template

模板定义

float

单精度浮点数类型

void

无返回类型

protected

访问修饰符(保护)

this

指向当前对象的指针

volatile

易变修饰符,禁止编译器优化

while

while循环

delete

释放动态内存分配的操作符

reinterpret_cast

重新解释类型转换

goto

无条件跳转语句

二、 命名空间

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

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
// C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
int main()
{
 printf("%d\n", rand);
 return 0;
}
代码语言:javascript
复制
// 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”

2.1 命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{ }即可,{ } 中即为命名空间的成员。

1. 正常的命名空间定义
代码语言:javascript
复制
// bit是命名空间的名字,一般开发中是用项目名字做命名空间名。
namespace bit
{
	// 命名空间中可以定义变量/函数/类型
	int rand = 10;
	int Add(int left, int right)
	{
		return left + right;
	}
	struct Node
	{
		struct Node* next;
		int val;
	};
}
代码语言:javascript
复制
int main()
{
	int a = 1;// 不同的域可以定义同名变量
	printf("%d\n", a);
	printf("%d\n", ::a);
	// :: 预作用限定符, _::标识符
	// _处为空时,默认访问全局变量
	printf("%d\n", bit::rand);

	return 0;
}
2. 命名空间可以嵌套
代码语言:javascript
复制
// test.cpp
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;
     }
 }
}
3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
代码语言:javascript
复制
// ps:一个工程中的test.h和上面test.cpp中两个N1会被合并成一个
// test.h
namespace N1
{
	int Mul(int left, int right)
	{
		return left * right;
	}
}

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

2.2 命名空间使用

代码语言:javascript
复制
// 编译默认查找
// a、当前局部域				: 自留地
// b、全局域找				: 村子野地
// c、到展开的命名空间中查找  : 相当于张大爷在自己的自留地加了声明,谁需要就来摘

// 指定展开某一个
//using bit::x;

// 三种
// 1、指定访问
// 2、全展开
// 3、指定展开某一个

// 局部域
// 全局域
// 命名空间域
// 不同域可以定义同名的变量/函数/类型

命名空间中成员该如何使用呢?比如:

代码语言:javascript
复制
namespace bit
{
	// 命名空间中可以定义变量/函数/类型
	int a = 0;
	int b = 1;
	int Add(int left, int right)
	{
		return left + right;
	}
	struct Node
	{
		struct Node* next;
		int val;
	};
}
int main()
{
	// 编译报错:error C2065: “a”: 未声明的标识符
	printf("%d\n", a);
	return 0;
}

命名空间的使用有三种方式:

  • 加命名空间名称及作用域限定符
代码语言:javascript
复制
int main()
{
    printf("%d\n", N::a);
    return 0;    
}
  • 使用using将命名空间中某个成员引入
代码语言:javascript
复制
using N::b;
int main()
{
    printf("%d\n", N::a);
    printf("%d\n", b);
    return 0;    
}
  • 使用using namespace 命名空间名称引入(展开命名空间)
代码语言:javascript
复制
using namespce N;
int main()
{
    printf("%d\n", N::a);
    printf("%d\n", b);
    Add(10, 20);
    return 0;    
}

三、C++输入&输出

新生婴儿会以自己独特的方式向这个崭新的世界打招呼,C++刚出来后,也算是一个新事物,那C++是否也应该向这个美好的世界来声问候呢?我们来看下C++是如何来实现问候的。

代码语言:javascript
复制
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}

说明:

  1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。
  2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。
  3. <<是流插入运算符,>>是流提取运算符。
  4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。
  5. 实际上cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载等知识,这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用。后面我们还有有一个章节更深入的学习IO流用法及原理。

注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间, 规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因 此推荐使用<iostream>+std的方式。

ps:关于cout和cin还有很多更复杂的用法,比如控制浮点数输出精度,控制整形输出进制格式等等。因为C++兼容C语言的用法,这些又用得不是很多,我们这里就不展开学习了。后续如果有需要,我们再配合文档学习。

std命名空间的使用惯例:

std是C++标准库的命名空间,如何展开std使用更合理呢?

1. 在日常练习中,建议直接using namespace std即可,这样就很方便。 2. using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对 象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模 大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。

四、 缺省参数

4.1 缺省参数概念

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

4.2 缺省参数分类

  • 全缺省参数

注意:使用缺省值,必须从右往左使用不能跳着用!!!

代码语言:javascript
复制
void Func(int a = 10, int b = 20, int c = 30)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }
  • 半缺省参数

注意:因为使用的时候是从右往左使用的,所以半缺省参数也必须从右往左!!!

代码语言:javascript
复制
void Func(int a, int b = 10, int c = 20)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

注意:

1. 半缺省参数必须从右往左依次来给出,不能间隔着给

2. 缺省参数不能在函数声明和定义中同时出现

代码语言:javascript
复制
//a.h
  void Func(int a = 10);
  
  // a.cpp
  void Func(int a = 20)
 {}
  
  // 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,
  // 那编译器就无法确定到底该用那个缺省值。

3. 缺省值必须是常量或者全局变量

4. C语言不支持(编译器不支持)

五、函数重载

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重 载了。 比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个 是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!”

5.1 函数重载概念

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

  • 在同一个作用域才有重载概念
代码语言:javascript
复制
// 不同作用域 可以同名
namespace bit1
{
	void Swap(int* pa, int* pb)
	{
		cout << "void Swap(int* pa, int* pb)" << endl;
	}
}

namespace bit2
{
	void Swap(int* px, int* py)
	{
		cout << "void Swap(int* pa, int* pb)" << endl;
	}
}

// 同一作用域 可以同名,满足重载规则
void Swap(double* pa, double* pb)
{
	cout << "void Swap(double* pa, double* pb)" << endl;
}

using namespace bit1;
using namespace bit2;
// 他们两依旧是ok,不是重载关系

int main()
{
	int a = 0, b = 1;
	double c = 0.1, d = 1.1;
	// 调用歧义
	//Swap(&a, &b);

	Swap(&c, &d);

	return 0;
}
5.1.1、参数类型不同
代码语言:javascript
复制
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;
}

int main()
{
	Add(10, 20);
	Add(10.1, 20.2);

	return 0;
}
5.1.2、参数个数不同
代码语言:javascript
复制
void f()
{
	cout << "f()" << endl;
}

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

int main()
{
	f();
	f(10);

	return 0;
}
代码语言:javascript
复制
void f()
{
	cout << "f()" << endl;
}

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

int main()
{
	f();

	return 0;
}
5.1.3、参数类型顺序不同
代码语言:javascript
复制
// 3、参数类型顺序不同
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;
}

int main()
{
	f(10, 'a');
	f('a', 10);

	return 0;
}

5.2 C++支持函数重载的原理--名字修饰(name Mangling)

  • 为什么C++支持函数重载,而C语言不支持函数重载呢?
  • 在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
  1. 实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?
  2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。(老师要带同学们回顾一下)
  3. 那么链接时,面对Add函数,链接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
  4. 由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,下面我们使用了g++演示了这个修饰后的名字。
  5. 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。
    • 采用C语言编译器编译后
    • 结果结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
    • 采用C++编译器编译后
    • 结果结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。
    • Windows下名字修饰规则
    • 对比Linux会发现,windows下vs编译器对函数名字修饰规则相对复杂难懂,但道理都是类似的,我们就不做细致的研究了。
  6. 通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修 饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
  7. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办 法区分。

今天就先到这了!!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、C++关键字(C++98)
  • 二、 命名空间
    • 2.1 命名空间定义
      • 1. 正常的命名空间定义
      • 2. 命名空间可以嵌套
      • 3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
    • 2.2 命名空间使用
    • 三、C++输入&输出
      • std命名空间的使用惯例:
        • std是C++标准库的命名空间,如何展开std使用更合理呢?
    • 四、 缺省参数
      • 4.1 缺省参数概念
        • 4.2 缺省参数分类
        • 五、函数重载
          • 5.1 函数重载概念
            • 5.1.1、参数类型不同
            • 5.1.2、参数个数不同
            • 5.1.3、参数类型顺序不同
          • 5.2 C++支持函数重载的原理--名字修饰(name Mangling)
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档