C++学习笔记(一)——入门基础(一)

简介: C++学习笔记(一)——入门基础

1. 命名空间


在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将

都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行

本地化,以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的。

如下图所示:

image.png

C语言是没有办法很好的解决此问题, 因此C++提出了一个新语法,命名空间.

1.1 命名空间定义


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

//1.普通的命名空间
//定义了一个叫qk的命名空间-域
namespace qk {
  //命名空间可以定义函数/变量/类型
  int rand = 1;//他们还是全局变量,放到静态区
  int sub(int a, int b)
  {
    return a - b;
  }
  struct node {
    struct node* next;
    int data;
  };
}
//命名空间可以嵌套
namespace n1 {
  int a;
  int b;
  int add(int m, int n)
  {
    return m + n;
  }
  namespace n2 {
    int c;
    int d;
    int sub(int g, int f)
    {
      return g - f;
    }
  }
}
//同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
namespace n3 {
  int mul(int a, int b)
  {
    return a * b;
  }
}

1.2 命名空间使用


首先说一下最常见的一种错误用法,例如:

namespace qk
{
   int a=10;
   int b=20;
   int sub(int c,int d)
 {
   return c-d;
 }
}
int main()
{
  printf("%d",a);//该编译错误,无法识别a
}

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

  • 加命名空间名称及作用域限定符(::)【安全性高,但使用起来不方便
int main()
{
  printf("%d",qk::a);
  return 0;
}
  • 使用using将命名空间中最常用的成员引入【安全性高,并且可以减少一些麻烦】
using N::b;
int main()
{
  printf("%d",b);
  return 0;
}

  • 使用using namespace命名空间引入 【安全性低,平常练习可以用用,但做项目不推荐使用
using namespace qk;
int main()
{ 
 sub(10,20);
 return 0;
}

易错区:

1.在命名空间中定义的成员是全局变量,并放在静态区;

2.命名空间可以初始化,但是不能赋值;

namespace qk
{
  int rand=10;//可以初始化
  rand=5;//但是不可以赋值
}

2. C++输入&输出


C++是包含C语言的,因此在C++中同样可以使用C语言输入和输出.

image.png

使用cout标准输出和cin标准输入时,必须包含<iostream>头文件以及std标准命名空间;

这里我直接使用using namespace将命名空间展开,平常练习代码推荐使用;

C++中的输入和输出更加方便,可以自动识别类型,小数点后有几位就几位不会向后估读;

endl和C语言中的换行符'\n'作用一样;

<<:流插入运算符,>>:流提取运算符;

3. 缺省参数


3.1 概念


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

image.png

3.2 缺省参数分类


  • 全缺省参数
//全缺省参数
void qk(int a = 10, int b = 20, int c = 30)
{
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  cout << "c=" << c << endl;
}
  • 半缺省参数
//半缺省参数 -- 缺省部分参数 -- 必须从右往左缺省,必须连续缺省
void qk(int a, int b = 20, int c = 30)
{
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  cout << "c=" << c << endl;
}

注意:

  • 半缺省参数必须从右往左依次给出,不能间隔着给;
  • 缺省参数不能在函数声明和定义同时给出;
  • 缺省值必须是常量或者是全局变量;
  • C语言不支持;
/这均是错误的写法
void qk(int a = 10, int b, int c)
void qk(int a = 10, int b, int c = 30)
qk(, 2, );
qk(, , 3);
//heap.h(声明)
void qk(int a = 10);
// heap.c(定义)
void qk(int a = 20)
{}
// 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那
//个缺省值。

4. 函数重载


自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。

比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!"

4.1 概念


函数重载:是函数的一种特殊情况,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;
}
//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;
}

下面函数属于函数重载吗?

//返回值不同,不能构成重载 -- 调用的时候不能区分
short Add(short left, short right)
{
  return left + right;
}
int Add(short left, short right)
{
  return left + right;
}
// 1、缺省值不同,不能构成重载
void f(int a)
{
  cout << "f()" << endl;
}
void f(int a = 0)
{
  cout << "f(int a)" << endl;
}
// 2、构成重载,但是使用时会有问题 : f(); // 调用存在歧义
void f()
{
  cout << "f()" << endl;
}
void f(int a = 0)
{
  cout << "f(int a)" << endl;
}
int main()
{
  // f(); // 调用存在歧义
   f(1);
  return 0;
}

4.2 名字修饰


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

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

image.png

image.png

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下gcc的修饰规则简单易懂,下面我们使用了gcc演示了这个修饰后的名字。

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

  • 采用C语言编译器编译后结果

image.png

结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。

  • 采用C++编译器编译后结果
  • image.png
  • 结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息
    添加到修改后的名字中。也理解了,为什么函数重载要求参数不同,跟返回值没关系。

【拓展学习:C/C++函数调用约定和名字修饰规则】

C++的函数重载 - 吴秦 - 博客园

C/C++ 函数调用约定_狮子的专栏-CSDN博客_c 调用约定

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

另外我们也了解了,为什么函数重载要求参数不同,而跟返回值没关系.

5. extern "C"(下一章做重点介绍)


有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译。

注意:在函数前加上"extern C"后,该函数便不能支持重载了。

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


相关文章
|
3月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
52 2
C++入门12——详解多态1
|
3月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
41 3
|
3月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
55 2
|
3月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
93 1
|
3月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
98 1
|
3月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
33 1
|
3月前
|
存储 编译器 C++
C++入门3——类与对象2-1(类的6个默认成员函数)
C++入门3——类与对象2-1(类的6个默认成员函数)
55 1
|
3月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
73 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
3月前
|
存储 安全 编译器
【C++打怪之路Lv1】-- 入门二级
【C++打怪之路Lv1】-- 入门二级
34 0
|
3月前
|
自然语言处理 编译器 C语言
【C++打怪之路Lv1】-- C++开篇(入门)
【C++打怪之路Lv1】-- C++开篇(入门)
39 0