【C++从0到王者】第一站:从C到C++(上)

简介: 【C++从0到王者】第一站:从C到C++

一、命名空间

1.C的命名缺陷

假设我们用c语言写了一段代码

#include<stdio.h>
int rand = 0;
int main()
{
  printf("%d\n", rand);
  return 0;
}

正常情况下,都会打印0

但是我们有时候会包stdlib这个头文件,我们发现报错了

根据上面的提示,我们不难发现,这是因为rand在stdlib这个头文件中已经被使用了。所以发生了重定义现象。

试想一下,一旦使用c语言去写一些大型项目,那么毫无疑问,会出现大量的重定义现象。

由此我们引入了namespace这个关键词,他的意思是命名空间

2.域和命名空间

我们在c语言的时候就了解过作用域的概念,比如下面的代码中,我们最后打印出来的结果是1,他是可以编译通过的。而打印出main函数里面定义的a的原因是因为局部优先。这个我们也称作局部域,而全局的a则位于全局域

#include<stdio.h>
#include<stdlib.h>
int a = 0;
int main()
{
  int a = 1;
  printf("%d\n", a);
  return 0;
}

我们的域可以分为类域,命名空间域,全局域,局部域。

有局部域,优先访问局部域,没有局部,优先访问全局的。

当局部域和全局域都不存在的时候且命名空间域存在,直接输出a是无法输出的,也就是说无法直接访问命名空间域

上面的是局部域不存在的情况,我们现在来讨论一下如果全局域和局部域同时存在的话,如何访问全局域?

c语言是无法实现这个的。除非使用指针等。

而在c++中为了实现这个,引入了::操作符,这个操作符的作用是域作用限定符,他的使用如下代码所示,即在a的前面使用::,在左边可以加上一个空格(当然不加也无所谓),我们代表从全局域中去访问

#include<stdio.h>
#include<stdlib.h>
int a = 0;
int main()
{
  int a = 1;
  printf("%d\n", ::a);
  printf("%d\n", a);
  return 0;
}

也就是说,通过这个操作符,我们可以实现无论是否有局部域,都可以去访问全局域

既然全局域的访问解决了,我们前面也提到过,命名空间域无法直接访问。那么该如何访问呢,答案是展开了命名空间域或者指定访问。

#include<stdio.h>
#include<stdlib.h>
int a = 0;
namespace boundary
{
  int a = 2;
}
int main()
{
  int a = 1;
  printf("%d\n", ::a);
  printf("%d\n", a);
  printf("%d\n", boundary::a);
  return 0;
}

如上所示是指定访问的方式

如下是展开命名空间的访问方式

那么如果即存在全局域又展开了命名空间域

则会出现不明确的现象

而如果不展开的话,那么就直接访问全局域

其实到了这里,我们也许已经混乱了。

但是其实核心如下:

1.优先局部域,其次全局域,最后访问展开的命名空间域或者指定的命名空间域

展开了命名空间域其实就相当于将这个变量给暴露在全局中,获得了全局的特点

2.如果是指定访问,那么没有特别需要注意的点,唯一需要注意的是,展开了命名空间域以后,他将获得全局的特点,如果使用全局的指定方式,那么将他可以访问这个变量

3.如果是直接访问,这个比较复杂

先看是否存在局部域,如果有局部域,那么直接访问局部域

如果没有局部域,那么就要再看是否存在全局域和命名空间域。

如果有全局域但没有命名空间域,那么直接访问全局域

如果没有全局域但存在命名空间域,如果没有展开,直接访问是无法访问到的,如果展开,直接访问可以访问到

如果全局域和命名空间域均存在,那么我们就要判断命名空间域是否展开

如果没有展开,访问全局域,如果展开,相当于两个相同命名的变量暴力在全局中产生冲突。

其次局部域不可以展开命名空间

3.命名空间的使用与嵌套

1.命名空间可以使用变量/函数/结构体

namespace N0
{
  // 命名空间中可以定义变量/函数/类型
  int rand = 10;
  int Add(int left, int right)
  {
    return left + right;
  }
  struct Node
  {
    struct Node* next;
    int val;
  };
}

2.命名空间可以嵌套

//2. 命名空间可以嵌套
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.同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
// ps:一个工程中的test.h和上面test.cpp中两个N1会被合并成一个
namespace N1
{
  int Mul(int left, int right)
  {
    return left * right;
  }
}

如上述的三种代码的嵌套等引用操作如下所示

int main()
{
  printf("%d\n", N0::rand);
  printf("%d\n", N0::Add(2, 3));
  printf("%d\n", N1::N2::Sub(1, 2));
  return 0;
}

二、输入输出

c++的输出是使用cout的

但是这个cout是定义在c++的标准库中的标准命名空间的,直接使用会报错的,我们想要使用这个输出,我们有两种方式可以做到

一种方式是直接展开命名空间,如下所示

另一种方式是使用指定访问

在使用第一种方式的时候,我们直接展开可能会展开冲突,这是很危险的。所以在项目中一般不直接展开

但是我们还有一种方式,就是将常用的给展开

我们现在来理解一下这个<<这个操作符,这个操作符在c语言中是移位操作符,在c++里面他又有了流插入运算符的含义

意思就是将"hello world "这个字符串插入到流中,而且我们还需要注意的是endl其实就是'\n',换行的意思,就是流插入hello world之后,在插入一个换行,将换行插入流

而且流插入可以使用多个类型

我们也可以发现cout的特点就是可以自动识别类型。

还有一个是输入cin,以及>>流提取运算符

他们的使用如下

#include<iostream>
//using namespace std;
using std::cout;
using std::endl;
using std::cin;
int main()
{
  int x = 10;
  double y = 11.11;
  cout << x << " " << y << endl;
  cin >> x >> y;
  cout << x << " " << y << endl;
  std::cout << "hello world" << std::endl;
}

需要注意的是,使用cin也许会出现精度缺失的现象,我们有时候也需要打印宽度和精度的数据,这里我们建议采用c语言的方法,而不采用c++的方法,因为c++的方法过于繁琐

而且printf和scanf是比c++的cout和cin快的。

三、缺省参数(默认参数)

1.缺省参数的概念

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

例如下面的代码

#include<iostream>
using namespace std;
void Func(int a = 0)
{
  cout << a << endl;
}
int main()
{
  Func(); //没有传参时,使用参数的默认值
  Func(10);//有传参时,使用指定的值
  return 0;
}

2.缺省参数分类

1>全缺省参数

如下代码所示,每一个参数都缺省了。这就是全缺省参数。在全缺省参数的传参中,我们如果需要指定值,必须要从左到右指定。中间不可以跳过一个传参。一旦出现一个缺省的,后面的必须都不可以指定了

#include<iostream>
using namespace std;
void Func(int a = 10, int b = 20, int c = 30)
{
  cout << a << ' ';
  cout << b << ' ';
  cout << c << ' ';
  cout << endl;
}
int main()
{
  Func();
  Func(1);
  Func(1, 2);
  Func(1, 2, 3);
  return 0;
}

比如说像下面这种是错误的

相关文章
|
存储 安全 编译器
【C++从0到王者】第一站:从C到C++(下)
【C++从0到王者】第一站:从C到C++(上)
47 0
|
6月前
|
存储 编译器 程序员
【C++初阶】第一站:C++入门基础(下)-2
【C++初阶】第一站:C++入门基础(下)-2
|
6月前
|
编译器 C++
【C++初阶】第一站:C++入门基础(下)-1
【C++初阶】第一站:C++入门基础(下)-1
|
6月前
|
C语言 C++
【C++初阶】第一站:C++入门基础(中)-2
【C++初阶】第一站:C++入门基础(中)-2
|
6月前
|
编译器 Linux C语言
【C++初阶】第一站:C++入门基础(中)-1
【C++初阶】第一站:C++入门基础(中)-1
|
6月前
|
编译器 C语言 C++
【C++初阶】第一站:C++入门基础(上) -- 良心详解-2
【C++初阶】第一站:C++入门基础(上) -- 良心详解-2
|
6月前
|
安全 Unix 编译器
【C++初阶】第一站:C++入门基础(上) -- 良心详解-1
【C++初阶】第一站:C++入门基础(上) -- 良心详解-1
|
自然语言处理 编译器 Linux
【C++从0到王者】第一站:从C到C++(中)
【C++从0到王者】第一站:从C到C++(上)
52 0
|
9天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
36 4
|
10天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
33 4