C++初阶——命名空间、缺省参数和函数重载

我们知道C语言的hello world文件后缀为’.c’,那么C++的文件后缀为’.cpp’
首先写出第一个C++程序Hello World来了解C++这门语言。

#include <iostream>
using namespace std;	//1.命名空间

int main()
{
    
    
  cout<<"Hello World!"<<endl;	//2.输出
  return 0;
}

从C++的hello world可以看出与C语言的直观区别

  • C语言中的头文件以’.h’结尾,而C++却是直接书写头文件名
  • C++中还多可一条语句using namespace std
  • C++中的输出语句为cout,换行语句为endl

那么接下来就从下面几个方面展开叙述C++的基础知识:

一、命名空间

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

举个栗子,解释命名冲突:

假设你和你的同事分别负责一个项目的不同模块,如果你定义了变量a你的同事也定义了变量a,那么程序在使用变量a时使用的是谁定义的变量呢?在C++中使用命名空间namespace来解决这个问题

1.1 命名空间的定义

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

1.1.1 普通的命名空间

命名空间中的内容既可以是变量定义,也可以是函数定义

//1. 普通的命名空间
namespace N1  // N1为命名空间的名称
{
    
    
   // 命名空间中的内容,既可以定义变量,也可以定义函数
   int a;
   int Add(int left, int right)
   {
    
    
       return left + right;
   }
}

1.2.2 命名空间的嵌套

可以N3嵌套在N2中

扫描二维码关注公众号,回复: 12868028 查看本文章
//2. 命名空间可以嵌套
namespace N2
{
    
    
   int a;
   int b;
   int Add(int left, int right)
   {
    
    
       return left + right;
   }
    
   namespace N3	//N3嵌套在N2中
   {
    
    
       int c;
       int d;
       int Sub(int left, int right)
       {
    
    
           return left - right;
       }
   }
}

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

下面N1的内容会和前面1中N1的内容合并到一个空间中

namespace N1
{
    
    
   int Mul(int left, int right)
   {
    
    
       return left * right;
   }
}

注意:
一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中,也就是只能在自己的命名空间中有效。

1.2 命令空间的使用

#include<stdio.h>

namespace N2
{
    
    
  int a = 10;
  int b = 20;
  int Add(int x, int y)
  {
    
    
    return x + y;
  }
  namespace N3
  {
    
    
    int c = 30;
    int d = 40;
    int Sub(int x ,int y)
    {
    
    
      return x - y;
    }
  }
}

int main()
{
    
    
  printf("%d\n", a);  // 该语句编译出错,无法识别a
  printf("%d\n", c);  // 该语句编译出错,无法识别c

  return 0;
}

上述程序如果直接使用a,b变量则会报错,我们以第二种为例来讲解命名空间使用的三种方式:

1.2.1 加命名空间名称及作用域限定符

在变量名前加命名空间名称N1和作用域限定符::就可以访问变量a和c了

int main()
{
    
    
  printf("%d\n", N2::a);
  printf("%d\n", N2::N3::c);

  return 0;
}

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

1.2.2 使用using将命名空间中成员引入

using N2::a;

int main()
{
    
    
  printf("%d\n", a);
  printf("%d\n", N2::N3::c);

  return 0;
}

在这里插入图片描述

访问变量a就以直接使用而不加N2::了

1.2.3 使用using namespace 命名空间名称引入

using namespace N2;

int main()
{
    
    
  printf("%d\n", a);
  printf("%d\n", N3::c);
  printf("%d\n", Add(20,30));

  return 0;
}

在这里插入图片描述

访问N2变量中变量与函数以及嵌套的命名空间可以不用输入N2::

二、输入&输出

  1. 使用cout标准输出(控制台)和cin标准输入(键盘)时,必须包含< iostream >头文件以及std标准命名空间。
  2. 使用C++输入输出更方便,不需增加数据格式控制,比如:整形–%d,字符–%c等
#include <iostream>
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;
}

在这里插入图片描述

三、缺省参数

3.1 缺省参数的定义

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

#include <iostream>

using namespace std;

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

int main()
{
    
    
  TestFun();	// 没有传参时,使用参数的默认值
  TestFun(20);  // 传参时,使用指定的实参
}

在这里插入图片描述

3.2 缺省参数分类

3.2.1 全缺省参数

所有参数全部具有默认值就是全全缺省参数

void TestFun1(int a=10, int b =20, int c=30)
{
    
    
  cout<<"a= "<<a<<endl;
  cout<<"b= "<<b<<endl;
  cout<<"c= "<<c<<endl;
}

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

}

在这里插入图片描述

3.2.2 半缺省参数

只给部分参数的默认值就是半缺省参数

void TestFun2(int a, int b=20, int c=30)
{
    
    
  cout<<"a= "<<a<<endl;
  cout<<"b= "<<b<<endl;
  cout<<"c= "<<c<<endl;
  
}

在这里插入图片描述
注意:
1. 半缺省参数必须从右往左依次来给出,不能间隔着给
2. 缺省参数不能在函数声明和定义中同时出现

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

3. 缺省值必须是常量或者全局变量
4. C语言不支持(编译器不支持)

四、函数重载

4.1 函数重载概念

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

#include <iostream>
using namespace std;

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

}
 
int main()
{
    
    
  Add(10, 20);
  Add(10.0, 20.0);
  Add(10L, 20L);

  return 0;

}

4.2 C++支持函数重载,而C语言不支持函数重载的原因

4.2.1 过程分析

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
这四个阶段具体的功能可以参考这篇博客第二节
《Linux的C语言开发工具——通过进度条小程序学习使用gcc、gdb和make/Makefile工具》
假设我们有以下程序

#include <iostream>
using namespace std;

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

}
 
int main()
{
    
    
  Add(10, 20);
  Add(10.0, 20.0);
  Add(10L, 20L);

  return 0;

}

如果使用C语言编译会发现由于函数名相同,编译不通过,而C++却可以编译通过
在这里插入图片描述
我们反汇编查看C和C++中对于函数名的修饰是怎样的?

  • 在Linux下使用以下命名就可以查看了C语言反汇编结果
    objdump -S 05-C_FunOverload

在这里插入图片描述
结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。

  • 在Linux下使用以下命名就可以查看了C++反汇编结果
    objdump -S 05-FunOverload
    在这里插入图片描述
    结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

4.2.2 结论

通过分析我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。

通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。

4.3 extern “C”

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

extern "C" int Add(int left, int right);
 
int main()
{
    
    
    Add(1,2);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40076022/article/details/113483132