开启C++之旅(上):探索命名空间与函数特性(缺省参数和函数重载)

简介: 开启C++之旅(上):探索命名空间与函数特性(缺省参数和函数重载)

之前浅显的讲解了数据结构的部分内容:数据结构专栏


那么今天我们迎来了新的起点:C++的探索之旅


1.命名空间


1.1引入命名冲突


在c中:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//定义一个全局变量
int rand = 1;
int main()
{
    srand(time(0));
    printf("%d\n", rand);
    return 0;
}

严格的编译器会直接报错


rand我们都知道是产生随机数的函数,现在我定义了一个全局变量rand,显然是有命名冲突

所以c++就提供了解决方案


1.2命名空间


想必学过c的大家第一次接触c++看到:

using namespace std;

都会想这是什么??大多老师都会让说:你们先记着这是固定的,以后会懂(结果到了期末考完也什么都没说)


namespace 是 C++ 中的关键字,用于创建命名空间,它是用来避免命名冲突并组织代码的一种机制。通过命名空间,可以将一系列的变量、函数、类等内容封装在其中,以便更好地组织代码


1.2.1命名空间的定义


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

  1. 命名空间的定义事例
namespace Test1
{
  // 命名空间中可以定义变量/函数/类型...
  int rand = 1;
  int Add(int left, int right)
  {
    return left + right;
  }
  struct ListNode
  {
    struct Node* next;
    int data;
  };
}
  1. 命名空间可以嵌套
namespace Test2
{
  int a = 0;
  namespace Test2_1
  {
    int a = 1;
  }
}
  1. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中(合并成一个)

一个工程中的test.h和上面test.cpp中两个N1会被合并成一个命名空间

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


1.2.2命名空间的使用


双冒号 :: 在 C++ 中是作用域解析运算符,它用于指定特定范围内的命名空间或类的成员。它的主要作用有两个:


命名空间限定:用于指定特定命名空间中的变量、函数或类。例如,std::cout 中的 std 是命名空间,cout 是该命名空间下的对象。

类作用域限定:用于指定类的成员函数或静态成员变量。在类的定义或类外部,双冒号可以用于访问类的静态成员

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


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

namespace Test1
{
  // 命名空间中可以定义变量/函数/类型...
  int rand = 1;
  int Add(int left, int right)
  {
    return left + right;
  }
}
int main()
{
    // 使用作用域限定符号直接访问命名空间中的成员
  printf("%d\n", Test1::rand);
  return 0;
}

成功输出了:

  • 使用using将命名空间中某个成员引入
namespace Test2
{
  int b = 0;
  namespace Test2_1//这里命名空间嵌套
  {
    int a = 1;
  }
}
using Test2::b;
int main()
{
  printf("%d\n", b);
  printf("%d\n",Test2:: Test2_1::a);//这样访问a
  return 0;
}


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

这就是我们经常看到的using namespace std;使用后使用std命名空间时就不需要加上std::,可以直接用了


2.c++的输入与输出


#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
//(直接 using namespace std有点不安全)
using namespace std;
int main()
{
  int a = 0;
  cin >> a;
  cout << a << endl;
  cout << "Hello world!!!" << endl;
  return 0;
}

使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间的使用方法使用std。

cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。

<<是流插入运算符,>>是流提取运算符。(cout<<就是流入到控制台 )

使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。

实际上cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载等知识(挖个坑,以后详细介绍)


3.缺省参数


3.1概念


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

void Function1(int a = 0)
{
  cout<<a<<endl;
}
int main()
{
  Func();     // 没有传参时,使用参数的默认值
  Func(10);   // 传参时,使用指定的实参
  return 0;
}


3.2缺省参数分类


**全缺省参数(函数声明或定义中都指定默认值)

void Function2(int a = 1, int b = 2, int c = 3)
{
  cout << "a = " << a << endl;
  cout << "b = " << b << endl;
  cout << "c = " << c << endl;
}
  1. 半缺省参数
void Function3(int a, int b = 2, int c = 3)
{
  cout << "a = " << a << endl;
  cout << "b = " << b << endl;
  cout << "c = " << c << endl;
}

注意:

  1. 半缺省参数必须从右往左依次来给出,不能间隔
  2. 缺省参数不能在函数声明和定义中同时出现,有函数声明一般选择函数声明
  3. 缺省值必须是常量或者全局变量


4.函数重载


学过Java的同学必然不陌生


4.1概念


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

  1. 函数参数类型不同
int Add(int a, int b)
{
  return a + b;
}
double Add(double a, double b)
{
  return a + b;
}
int main()
{
  cout<<Add(10, 20)<<endl;
  cout<<Add(10.1, 20.2);
  return 0;
}
  1. 函数参数个数不同
void f()
{
  cout << "f()" << endl;
}
void f(int a)
{
  cout << "f(int a)" << endl;
}
  1. 函数参数类型顺序不同(也可认为属于类型不同)
void f(int a, char b)//先int后char
{
  cout << "f(int a,char b)" << endl;
}
void f(char b, int a)//先char后int
{
  cout << "f(char b, int a)" << endl;
}

"f(char b, int a)" << endl;

}


4.2C++支持重载的原理----名字修饰


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


链接器看到Test.o调用某个函数,但是没有函数的地址,就会到Func.o的符号表中找函数的地址,然后链接到一起。而使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则


c语言链接函数地址时(找函数)是靠函数名,所以不允许重名函数


c++中编译器需要为每个函数生成一个唯一的标识符来标记函数的地址。在 Linux 下,这些标识符是通过一种名为名字修饰(Name Mangling)的方式来生成的:


_Z + 函数名字符个数 + 函数名 + 每个参数类型首字母


所以重载函数虽然函数名相同,但是在链接函数地址时所依靠的标识符却不同


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

如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分

今天步入c++的学习啦,就先到这里!!!


目录
相关文章
|
4月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
94 0
|
7月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
356 6
|
12月前
|
程序员 C++
C++中的函数重载有什么作用
【10月更文挑战第19天】C++中的函数重载有什么作用
161 3
|
12月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
12月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
445 6
|
12月前
|
编译器 程序员 C++
C++中的函数重载是什么
【10月更文挑战第19天】C++中的函数重载是什么
287 0
|
8月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
4月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
170 0
|
6月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
181 12
|
7月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
131 16