C++ 入门基础(2)

发布于:2025-08-01 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

1. 命名空间

1.1 命名空间的定义

1.2 基本用法

1.2.1 域

1.2.1.1 域的定义

1.3 作用域解析运算符

1.4 注意事项

1.5 命名空间的使用

2. C++输入和输出

3. 缺省参数

3.1 缺省参数的定义

3.2 缺省参数的用法

4. 总结:


1. 命名空间

在上篇文章我们主要详细介绍了C++,围绕着C++的发展历程重要性及应用展开了主要的介绍。今天我们就要学习C++的第一个知识点——命名空间

在引入命名空间之前,小编想先展示一段代码,通过这段代码带领我们进入命名空间的世界。

#include <stdio.h>
#include <stdlib.h>

int rand = 10;

int main()
{
    // 编译报错: error C2365: “rand”: 重定义;以前的定义是“函数”
    printf("%d\n", rand);
    return 0;
}

如上面的代码所示,上面的代码如果放在编译器中是会报错的,为什么呢?

在 C 语言里, <stdlib.h>  头文件中声明了 rand  函数(用于生成随机数 ),而代码里又定义了全局变量 int rand = 10; ,这就导致命名冲突——编译器看到 rand  时,不知道该用变量 rand  还是标准库的 rand  函数,所以编译报错 “ rand : 重定义;以前的定义是‘函数’” 。

C语言项目类似上面程序这样的命名冲突是普遍存在的问题,C++引入namespace就是为了更好的解决这样的问题。

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

1.1 命名空间的定义

C++的命名空间(namespace) 是一种用来避免命名冲突的机制,简单说就是给变量、函数、类等起的名字“分组”,让不同组里的同名标识可以共存。

1.2 基本用法

- 定义命名空间:用 namespace 关键字,把相关的代码包起来。
namespace csdn
{
    int rand = 10;

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

    struct Node
    {
        struct Node* next;
        int val;
    };
}

- 定义命名空间,需要使用到 namespace 关键字,后面跟命名空间的名字,然后接一对 {} 即可,{} 中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。

1.2.1 域

1.2.1.1 域的定义

在C++中,域(Scope,通常译为作用域)是指程序中某个标识符(变量、函数、类等)的有效范围。它决定了在哪里可以访问某个标识符,以及该标识符的生命周期从何时开始到何时结束。

  • namespace本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。
  • 在C++中,“命名空间”和“域(作用域,Scope)”都和“名字的可见范围”有关,但本质不同,关系可以概括为:命名空间是一种人为定义的、可扩展的域,而域是一个更宽泛的概念,包含了命名空间在内的多种范围类型。

C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。

  • 补充:
  • 常见的域包括:
  • - 局部域(比如函数内部的变量,只在函数内可见)
  • - 类域(类的成员变量/函数,只在类内部或通过对象访问)
  • - 命名空间域(由 namespace 定义的范围,比如 std 就是标准库的命名空间域)
  • - 全局域(不在任何类、函数、命名空间内的名字,整个程序可见)
  • - 命名空间 是C++专门为解决命名冲突设计的一种“域”,它是人为创建的、可以包含变量、函数、类等的范围,本质上是一种用户自定义的域。
  • 简单类比:域就像“地理范围”(比如国家、省份、城市),而命名空间就像“人为划分的行政区”(比如某个开发区),它属于地理范围的一种,但更灵活——可以嵌套(比如 namespace A { namespace B { ... }} ),也可以跨文件扩展,专门用来管理名字的可见性。

1.3 作用域解析运算符

在C++中, ::  被称为作用域解析运算符,用来指定“某个名字属于哪个域(作用域)”,明确告诉编译器要使用哪个范围里的名字。
 

  • 常见用法有:
  • 1. 访问命名空间中的成员
  • 比如  std::cout ,表示  cout  这个对象属于 std  命名空间(C++标准库的命名空间)。
  • 2. 访问类中的静态成员或嵌套类型
  • 例如在类  Person  中有一个静态函数  getCount() ,可以用  Person::getCount()  调用,表明这个函数属于  Person  类域。
  • 3. 访问全局域中的名字
  • 如果局部变量和全局变量重名,用  :: 变量名表示使用全局域的那个变量。比如:
int a = 10; // 全局变量
int main() {
    int a = 20; // 局部变量
    cout << a;      // 输出 20(局部变量)
    cout << ::a;    // 输出 10(全局变量)
}
  • 简单说, ::  就像一个“路径指示符”,告诉编译器“去哪个范围找这个名字”,避免混淆。

1.4 注意事项

  • 1. namespace只能定义在全局,当然他还可以嵌套定义。
  • 1. 命名空间的定义位置
  • 全局作用域:这是命名空间最常见的定义位置。在所有函数、类、块之外定义的命名空间,属于全局作用域的一部分,例如标准库的 std 命名空间。
  • 不允许在局部作用域定义:命名空间不能在函数内部、块内部(如 if / for 的 {} 内)等局部作用域中定义。这是因为命名空间的设计目的是用于全局或大尺度的代码组织,而非局部临时使用。
  • 2. 命名空间的嵌套定义
  • 命名空间支持嵌套,即一个命名空间内部可以包含另一个命名空间,这是C++明确允许的,主要用于更细致的代码分类:

 

namespace A 
{          // 全局作用域的命名空间A
    namespace B 
    {      // 嵌套在A中的命名空间B
        int x = 10;
    }
}
// 使用时需通过嵌套路径访问
int main() {
    std::cout << A::B::x;  // 输出10
    return 0;
}
  • 这种嵌套可以避免多层代码的命名冲突,比如大型项目中按模块划分一级命名空间,再按功能划分二级命名空间。
  • 2. 项目工程中多文件中定义的同名namespace会认为是一个namespace,不会冲突。
  • C++标准规定:同一个命名空间可以在多个不同的文件中分散定义,编译器会将这些分散的定义合并为一个完整的命名空间。

  •  假设项目中有两个文件:
  •  file1.cpp  中定义:
namespace MyProject 
{
    int a = 10;  // 命名空间 MyProject的一部分
}
  • -  file2.cpp  中定义:
namespace MyProject   
{
    void func()    //与file1中的MyProject是同一个命名空间
    {}         
}
  • 编译器会将两者合并,相当于:
namespace MyProject {
    int a = 10;
    void func() 
    {}
}
  • 使用时, MyProject::a  和  MyProject::func()  可在整个项目中统一访问,不会因定义在不同文件而冲突。
  • 3. C++标准库都放在一个叫std(standard)的命名空间中。
  • C++标准库:
  • C++标准库是C++语言官方规定的一套现成的工具集合,里面包含了大量已经写好的代码,程序员不用自己编写,直接就能用。
  • 它就像一个“工具箱”,里面有:
  • 数据结构:比如存放数据的 vector (动态数组)、 map (键值对集合)等,不用自己手动实现链表、哈希表了。
  • 输入输出工具:比如 cout (输出内容到屏幕)、 cin (从键盘读取输入),负责程序和用户的交互。
  • 字符串处理:比如 string 类,能方便地拼接、截取字符串,比C语言的字符数组好用得多。
  • 算法:比如排序( sort )、查找( find )等,直接调用就能完成复杂操作。
  • 其他功能:比如时间处理、数学计算、文件操作等。
  • 简单说,标准库就是C++官方提供的“现成代码”,目的是让程序员少写重复代码,提高开发效率。
  • std 命名空间
  •  std 是“standard”(标准)的缩写,它是C++标准库专用的命名空间。
  • 前面说过,命名空间的作用是“隔离代码,避免重名冲突”。标准库包含了太多工具(比如 vector 、 cout ),如果这些工具直接放在全局作用域,很可能和你自己写的变量/函数重名(比如你可能也想定义一个叫 vector 的函数)。
  • 所以C++规定:标准库的所有工具都必须放在 std 这个命名空间里。
  • 比如:
  • 标准库的输出工具 cout ,完整名字是 std::cout ( std:: 表示“来自 std 命名空间”)。
  • 标准库的动态数组 vector ,完整名字是 std::vector 。
  • 这样一来,即使你自己定义了一个叫 cout 或 vector 的东西,也不会和标准库的工具冲突——因为标准库的工具被 std “包起来”了。
  • 总结:
  • C++标准库:官方提供的现成工具集合(数据结构、函数等),直接用能省时间。
  • std 命名空间:专门用来存放标准库工具的“容器”,防止这些工具和你写的代码重名冲突。
  • 使用标准库时,需要用 std:: 开头(比如 std::cout ),或者用 using namespace std; 简化(但不推荐在大型项目中用)。
  • C++标准库将所有组件(如函数、类、模板等)都放在 std 命名空间中,这是C++标准明确规定的设计,主要目的和作用如下:
  • 1. 避免命名冲突
  • C++标准库包含大量常用组件(如 cout 、 vector 、 string 等),如果这些组件直接暴露在全局作用域中,很可能与用户自定义的标识符(变量、函数名等)重名,导致冲突。
  • 例如,若用户自己定义了一个名为 vector 的函数,而标准库的 vector 容器也在全局作用域,编译器就无法区分两者,引发错误。
  •  std 命名空间通过将标准库组件“隔离”在独立空间中,从根本上避免了这种冲突。
  • 2. 明确标识标准库组件
  •  std 是“standard”(标准)的缩写,使用 std:: 前缀(如 std::cout 、 std::vector )可以清晰区分:这是标准库提供的组件,而非用户自定义或其他库的内容。
  • 这增强了代码的可读性和可维护性,尤其是在大型项目中,能让开发者快速识别组件的来源。
  • 3. 符合命名空间的设计初衷
  • 命名空间的核心功能就是“组织代码、隔离作用域”,标准库作为C++的官方组件,自然会遵循这一机制。将所有标准库内容统一放在 std 中,是对命名空间功能的典型应用,也为用户提供了规范的代码组织范例。
  • 总结
  •  std 命名空间是C++标准库的“专属容器”,它通过隔离作用域避免了命名冲突,同时明确标识了标准库组件,是C++为规范代码组织、提升兼容性而设计的重要机制。使用标准库组件时,需通过 std:: 前缀访问(或通过 using 声明简化)。

1.5 命名空间的使用

编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面程序会编译报错。所以我们要使用命名空间中定义的变量/函数,有三种方式:

  • 指定命名空间访问,项目中推荐这种方式。
  • using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。
  • 展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。

2. C++输入和输出

C++ 的输入输出(I/O)系统主要依赖 <iostream>  头文件,核心是 cin (输入)和 cout (输出),它们属于 std  命名空间,以下详细拆解其用法、原理和特性:

  • 1. 基础概念与头文件
  • <iostream> :
  • 是 Input/Output Stream(输入输出流)的缩写,包含了 C++ 标准输入输出的核心定义,比如  cin(关联标准输入设备,通常是键盘)、 cout(关联标准输出设备,通常是屏幕)、 cerr(标准错误输出,直接输出到屏幕,不经过缓冲区)等。使用时需  #include <iostream>  引入。
  • 命名空间  std :
  • C++ 标准库的组件(包括  cin / cout )都放在  std  命名空间下,避免和用户自定义的变量/函数重名。因此直接用  cout  会报错,需写成  std::cout ;或用  using namespace std;  简化(小项目/练习常用,但大型项目不推荐,避免命名冲突)。
  • 2. 核心对象: cout (输出)
  •  cout 是 ostream 类的对象,用于向标准输出(屏幕)打印内容,配合  <<  流插入运算符 使用,语法:
  • std::cout << 数据1 << 数据2 << ... << 操纵符;
  • 基础用法:
  • 自动识别变量类型,无需像 printf 那样手动指定格式(如  %d / %f )。示例:
    int a = 10;
    double b = 3.14;
    std::cout << "整数:" << a << ",浮点数:" << b << std::endl;
    // 输出:整数:10,浮点数:3.14

  • 常用操纵符:
  • std::endl :
  • 本质是一个函数,作用是插入换行符  \n  + 刷新输出缓冲区(确保内容立刻显示到屏幕)。也可用  \n  替代换行,但  \n  不会主动刷新缓冲区。
  • 3. 核心对象: cin (输入)
  •  cin  是  istream  类的对象,用于从标准输入(键盘)读取数据,配合  >>  流提取运算符 使用,语法:
  • std::cin >> 变量1 >> 变量2 >> ...;

  • - 基础用法:
  • 自动识别变量类型,按空白符(空格、换行、制表符)分割输入,跳过空白符直到遇到有效数据。示例:
    int a;
    double b;
    std::cin >> a >> b;
    // 输入:10 3.14(按回车结束),a=10,b=3.14
  • - 注意事项:
  • - 遇到无效输入(如给  int  变量输入字母)时, cin  会进入错误状态,后续输入会被阻塞。需用  cin.clear()  重置状态, cin.ignore()  清除缓冲区错误数据。
  • 4. 和 C 语言  printf/scanf  的对比
  • 特性 C++( cin/cout ) C( printf/scanf )
    类型支持 自动识别类型(依赖函数重载) 手动指定格式( %d / %f  等)
    自定义类型支持 << / >>  支持自定义类输入输出 无法直接支持,需拆分为基础类型 
    代码简洁性 无需记忆格式符,写法更简洁 需精确匹配格式符,易出错
    效率 默认带缓冲区,效率稍低(可通过代码优化) 格式化灵活,但手动控制格式较繁琐 
    命名空间依赖 属于  std  命名空间,需处理作用域 直接在全局作用域( <stdio.h> )

总结:

  1. <iostream>是 Input Output Stream 的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。
  2. std::cin 是 istream 类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。
  3. std::cout 是 ostream 类的对象,它主要面向窄字符的标准输出流。
  4. std::endl 是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。
  5. <<是流插入运算符,>>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)
  6. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动指定格式,C++的输入输出可以自动识别变量类型(本质是通过函数重载实现的,这个以后会讲到),其实最重要的是C++的流能更好的支持自定义类型对象的输入输出。
  7. cout/cin/endl等都属于C++标准库,C++标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。
  8. 一般日常练习中我们可以 using namespace std,实际项目开发中不建议 using namespace std。
  9. 这里我们没有包含<stdio.h>,也可以使用printf和scanf,在包含间接包含了。vs系列编译器是这样的,其他编译器可能会报错。

3. 缺省参数

3.1 缺省参数的定义

C++ 中的 缺省参数(Default Arguments)是一个实用特性,允许函数声明或定义时为参数指定默认值。调用函数时,若省略带缺省值的参数,编译器会自动用默认值填充,能简化调用、提升代码灵活性。

3.2 缺省参数的用法

  • 1. 核心概念:给参数“预设值”
  • 函数声明/定义时,可为右侧参数指定默认值。调用时:
  • 若传入实参,用传入的值;
  • 若省略,用默认值。
  • 语法示例(声明时指定缺省):
  • void func(int a, int b = 10, int c = 20); // b、c 有默认值

  • 调用方式:
  • func(1);    // a=1, b=10(用默认), c=20(用默认)
    func(1, 2); // a=1, b=2, c=20(用默认)
    func(1, 2, 3); // a=1, b=2, c=3(全用传入值)
  • 2. 规则与限制
  • 右侧优先原则
  • 缺省参数必须从右往左连续设置,不能跳过中间参数。错误示例:
  • void func(int a = 10, int b); // 错误!a 设了默认,但右侧 b 没设,违反“右侧连续”规则

  • 正确写法需保证有缺省值的参数都在无缺省值参数的右侧:
  • void func(int a, int b = 10, int c = 20); // 合法:b、c 连续在右侧

  • 声明/定义只能选一处完整指定
  • 缺省参数不能在声明和定义中重复设置,否则编译器会认为“默认值冲突”。
  • - 场景 1:头文件声明 + 源文件定义
  • 头文件( .h )声明时给默认值,源文件( .cpp )定义时不能重复给:
    // test.h(头文件)
    void func(int a, int b = 10); 
    // test.cpp(源文件)
    void func(int a, int b) 
    { 
        // 定义时不写默认值,避免冲突
        // ... 
    }

  • - 场景 2:仅声明或仅定义(如 inline 函数)
  • 若函数直接定义(如 inline 函数写在头文件),可直接在定义处给默认值:
    inline void func(int a, int b = 10)
    { 
        // ... 
    }

  • 缺省值必须是“编译期可确定的常量”
  • 默认值可以是:
  • - 字面值( 10 、 3.14 、 "hello"  等);
  • -  const  常量、 constexpr  变量;
  • - 全局/静态变量(需注意作用域)。
  • 错误示例(运行时才能确定的值,无法作为缺省):
    int getDefault() { return 10; }
    void func(int a = getDefault()); // 错误!默认值需编译期确定

总结:

  •  缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。(有些地方把缺省参数也叫默认参数)
  • 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
  • 带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。
  • 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。
  • 缺省参数是 C++ 简化函数调用、扩展接口兼容性的实用工具,核心规则是右侧连续设置、声明/定义只设一次、默认值编译期确定。
  • - 日常开发中,用它给高频函数加“默认选项”,能减少重复代码;
  • - 大型项目里,新增接口参数时用缺省值兼容旧代码,可降低重构成本;
  • - 需注意与函数重载的分工、类继承中静态绑定的坑,避免引发意外行为。
  • 合理使用缺省参数,能让代码更简洁、更易维护,是 C++ 工程师必备的语法糖之一 

4. 总结:

以上便是关于C++入门基础的第一部分内容,主要围绕着命名空间,C++的输入和输出以及缺省参数这三个模块展开详细讲解,这三个语法知识对于初学C++来说都非常至关重要。一定要了解并熟练掌握它们,为后面更难的学习内容打下坚实的基础。最后感谢大家的观看!