C++ 入门基础(2)
目录
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 { // 全局作用域的命名空间Anamespace B { // 嵌套在A中的命名空间Bint x = 10;} } // 使用时需通过嵌套路径访问 int main() {std::cout << A::B::x; // 输出10return 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> )
总结:
- <iostream>是 Input Output Stream 的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。
- std::cin 是 istream 类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。
- std::cout 是 ostream 类的对象,它主要面向窄字符的标准输出流。
- std::endl 是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。
- <<是流插入运算符,>>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)
- 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动指定格式,C++的输入输出可以自动识别变量类型(本质是通过函数重载实现的,这个以后会讲到),其实最重要的是C++的流能更好的支持自定义类型对象的输入输出。
- cout/cin/endl等都属于C++标准库,C++标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。
- 一般日常练习中我们可以 using namespace std,实际项目开发中不建议 using namespace std。
- 这里我们没有包含<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++来说都非常至关重要。一定要了解并熟练掌握它们,为后面更难的学习内容打下坚实的基础。最后感谢大家的观看!