C++起步-命名空间解决命名冲突与cin-cout缺省参数

C++ 起步:从命名冲突到命名空间,cin/cout 与缺省参数

我的github:(https://github.com/xcx55/ubuntu-linux-project)

感谢各位大佬参观我的github!!

学完 Linux 系统编程回头看 C++,最大的感受是:C++ 的每个新语法都不是凭空发明的,都是在替 C 语言填坑。这篇从第一个坑写起------命名冲突。

一、C 语言的痛:库和你,撞名了

C 语言里有个经典死局:

c 复制代码
#include <stdio.h>
#include <stdlib.h>   // 库里有 rand 函数

int rand = 10;        // 你定义了一个叫 rand 的变量

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

问题不在于你写错了什么------你只是想定义一个叫 rand 的变量,但库早就把这个名字占了。C 语言只有一个全局域,你的变量和库的函数都住在这个域里,名字一撞就是重定义,而且这仗你打不赢:库你改不了,变量名你还非起不可。

项目越大,这个问题越致命。多人协作、多个库互相包含,谁也不能保证自己的名字和别人的不撞。

C++ 的解法:提出"域"的概念------不同域里可以命名相同的名字。namespace 就是单独命名出来的一个域。

二、namespace:只是改变了查找规则

先看域操作符 :::

c 复制代码
int x = 0;

int main()
{
    int x = 1;
    printf("%d\n", x);   // 1,就近找局部域
    printf("%d\n", ::x); // 0,::前面为空 = 全局域
    return 0;
}

编译器寻找一个名字,默认只在局部域 → 全域 这条链上找,不会主动去命名空间里找。想让它去 namespace 里找,就得显式指定:namespace名字::变量。

这里有个我很喜欢抠的细节:namespace 并没有改变变量的存储位置 ------命名空间域里定义的东西,本质上还是全局的。namespace 解决的是"全局域里的重命名冲突",它影响的仅仅是编译器的查找规则,而不是变量的性质。这一点想通了,后面很多疑惑就没了。

C++ 里域一共四种:全局域、局部域、类域、命名空间域。命名空间还可以嵌套:

c 复制代码
namespace bit
{
    namespace p1
    {
        int x = 0;
    }
}

// 访问:bit::p1::x

嵌套解决了另一个问题:同一个命名空间内部也可能撞名,那就再套一层,按"域1::域2::变量"的路径去找。

三、三种使用方式,项目里用哪一种?

多文件工程里(.h / .cpp 分开写),只要 namespace 名字相同,多个文件里的同名命名空间会自动合并------所以工程上习惯用一个统一的命名空间装项目自己的东西。

使用命名空间有三种姿势:

  1. 指定访问 :bit::x,每次都写全路径。麻烦,但最安全------项目中推荐这种方式;
  2. 部分展开 :using bit::x; 只放行某一个成员,对经常访问、确定不冲突的成员用这个;
  3. 全部展开 :using namespace bit; 图省事,把整个命名空间摊平到全局。项目里不推荐,冲突风险很大,日常小练习为了方便才用。

std 这个标准库命名空间也一样:日常练习可以 using namespace std,实际项目里别这么干。

四、cin / cout:重新设计的流

C++ 重新设计了一套输入输出------iostream。cout 是流插入、cin 是流提取(<< >> 这两个符号,C 语言还在拿它们做左移右移位运算,靠的是运算符重载,这是后话)。

它比 printf/scanf 好在哪?自动识别类型,不用占位符:

c 复制代码
int a = 10;
double d = 1.1;
cout << a << " " << d << endl;  // 不用写 %d %lf

printf 要人肉指定格式,%d 对应 int、%f 对应 float,类型一多就是查表游戏;cout 直接把对象丢进去就行。这个"自动识别"本质是函数重载实现的------同一名字的不同版本函数各接一种类型,编译器帮你选。更重要的是,C++ 的流能更好地支持自定义类型对象的输入输出 :你自己写的类,重载一下 << 就能直接打印,printf 永远做不到这件事(它的格式串只认识内置类型)。

细节:cout/cin 都在 std 命名空间里,所以用它们绕不开命名空间;cin 的工作是"从缓冲区读字符,转化成你的类型",cout 是"把内存里的东西转成字符流出去"------方向永远以流为中心。另外 cout/cin 的效率其实不如 printf/scanf,日常够用,追求性能时有专门手段(sync_with_stdio)去调。

顺带还个旧账:之前在文件操作章欠下的"流"的理解,到这里算是正式补上了------文件流和网络编程里读写的本质也是流,一脉相承。

五、缺省参数:没传参就吃默认值

c 复制代码
void func(int a = 10)
{
    cout << a << endl;
}

func();     // 10,没传就用缺省值
func(20);   // 20,传了就用你的

规则一句话:传了参就用参,没传就用缺省参数。全缺省、半缺省(从右往左连续给默认值)都支持。它和函数重载的恩怨下一篇细说------这两者组合起来会有调用歧义的坑,C++ 是有意这么设计的。


写 C 的时候,名字冲突这件事只能靠"人肉加前缀"来防( strutcpy、my_strcpy......);C++ 用一个 namespace 把它变成了语法层面的解。学习路线也是这个逻辑:先看见痛点,再看语言怎么收。下一篇写函数重载和引用------后者号称"指针的语法糖",但糖衣底下藏着 C++ 最重要的一条规则:临时变量具有常性。

相关推荐
hljqwb38 分钟前
java服务异常日志只打印异常类型,没有堆栈定位分析(十)
java·开发语言
ym hyd 11142 分钟前
生鲜超市库存管理信息系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·vue.js·spring boot·毕设
haerapi44 分钟前
KingbaseES 全文检索实战之前:先建立中文搜索的可解释基线
开发语言·python·全文检索
徐小黑ACG44 分钟前
Golang 基础02 控制语句
开发语言·后端·golang
liyunlong-java1 小时前
敏感词过滤完整指南(DFA 字典树)
java·开发语言·spring
(Charon)1 小时前
【C++面试】四种cast转换:static_cast、dynamic_cast、const_cast与reinterpret_cast
开发语言·c++·面试
脉动数据行情11 小时前
Python asyncio 异步实现比特币 BTC 实时行情监听 高并发版
开发语言·python·区块链
爱吃奥利奥_wen1 小时前
排序算法通关手册:从 O(n²) 到 O(n log n),用一篇文章打通算法内功心法
开发语言·经验分享
外收内放1 小时前
Python基础语法练习题(55-56)
开发语言·python