🔷 第十章 类和对象
📄 一句话概括

使用命名空间里的名字,一共三种方式:① 写全名(std::cout,最清楚、工程首选);② using 声明(using std::cout;,只放行一个名字,折中推荐);③ using namespace 指令(using namespace std;,整个放行,省字但风险大)。此外还有第四种实用工具:命名空间别名(namespace fs = std::filesystem;)——专治"名字太长"。

📝 本节和前面几节的关系

10.6 讲了"命名空间是什么、为什么需要",本节把"怎么用"说全。这里是 6.4 作用域思路的又一次应用:using 声明/指令都只在"它所在的作用域内"有效——写在函数里,就只影响那个函数。这个规律很重要,它让你可以"在局部放心地简化"。

一、三种使用方式(含优缺点)

方式 ① 写全名——最清楚

std::cout << "hello" << std::endl;
math::square(4);
优点 缺点
一眼看出"这个名从哪来";不会撞名;头文件里也安全 打得长,代码稍显啰嗦

工程上这是默认推荐的做法。 很多公司代码规范明确要求"不许写 using namespace"。

方式 ② using 声明——只放行一个名字

using std::cout;              // 只把 cout 放出来
using std::endl;

cout << "hello" << endl;      // ✅ 能用
// string s;                  // ❌ 不行:string 没被放行
std::string s2;               // 还得写全名

要点:

点 说明
一次只放行一个名字 想用几个就写几行
它的作用 相当于"在本作用域里,给这个名字装一个直通入口"
推荐场景 函数内部的局部简化(风险最小)

这是三种里最平衡的一种:省了字,风险又可控。

方式 ③ using namespace 指令——整个放行

using namespace std;          // std 里所有名字都放出来
cout << "hello" << endl;
string s;
优点 缺点
最省字 放出成千上万个名字,撞名风险最大;放头文件里就是灾难

用途:练习、小程序、教程示例。别用在头文件、别用在正经项目里(10.6 讲的)。

二、三者对比表

写全名 using std::cout; using namespace std;
写多长 长 中 最短
放行范围 无(不改动任何东西) 一个名字 整个命名空间
撞名风险 无 小(只那一两个名字) 大
头文件里能用吗 ✅ 能 ⚠️ 尽量别(会影响包含者) ❌ 绝对别
推荐程度 工程首选 局部简化首选 只在练习里用

三、作用域决定 using 的范围

这是本篇最实用的一条规律:

using 声明和指令,只在"它所在的那个作用域"内有效。(6.4 的知识)

#include <iostream>

void f1() {
    using namespace std;         // 只在 f1 里有效
    cout << "f1 里能直接用" << endl;
}

void f2() {
    // cout << "x";              // ❌ f2 里不行:f1 里的 using 管不到这儿
    std::cout << "f2 里得写全名" << std::endl;
}

int main() {
    f1();
    f2();

    {                             // 一个块
        using std::cout;          // 只在这个块里有效
        cout << "块里也能用" << endl;
    }
    return 0;
}

这条规律的两个好处:

好处 说明
可以"局部放开" 在个别函数里图省事,不影响别的函数
可以"用完就收" 出了那个块,using 就失效了——风险被关在小盒子里

好习惯:实在懒得写全名,就把 using namespace std; 写进函数体里,别写在文件开头。 这样它的影响范围从"整个文件"缩小到"一个函数"——同样是省事,风险小一个数量级。

四、第四种工具:命名空间别名

问题:有些命名空间名字又长又有层次:

std::filesystem::path p = "C:/data";
my_company::network::protocol::HttpClient client;

解法:起个外号——

namespace fs = std::filesystem;                  // fs 就是它的别名

fs::path p = "C:/data";                          // 清爽多了

再举一例:

namespace net = my_company::network::protocol;

net::HttpClient client;                          // 一下子短了很多

三条要点:

点 说明
写法 namespace 别名 = 原名;(别名在前,和 typedef 的 8.2 一个风格)
作用域 和 using 一样,只在本作用域内有效
用途 长名字的命名空间(尤其嵌套的,10.6.4 会遇到)

它和 using 的区别:using 是"把名字放出来",别名是"换个短称呼"。别名从不清空"谁属于谁"这层信息(还得写 fs::),所以更安全。长名字场景首选它。

五、动手 Demo:三种方式 + 别名的完整对照

#include <iostream>

namespace company {
    namespace hr {                       // 嵌套命名空间(10.6.4)
        void hire() { std::cout << "HR:发出录用通知" << std::endl; }
        void fire() { std::cout << "HR:办离职手续" << std::endl; }
    }
    void report() { std::cout << "公司:出季度报表" << std::endl; }
}

int main() {
    // ① 写全名(工程首选)
    company::report();
    company::hr::hire();

    // ④ 别名:给长名字起外号
    namespace hr = company::hr;          // 在本作用域内
    hr::fire();
    hr::hire();                          // 用别名调,一样能用

    // ② using 声明:只放行一个
    {
        using company::report;
        report();                        // 只在这个块里能这么写
    }
    // report();                         // ❌ 出了块就不行了

    // ③ using namespace:整个放行(写在局部,风险可控)
    {
        using namespace company;
        report();                        // 能用了
        std::cout << "(这一段里 company 的名字都放行了)" << std::endl;
    }

    std::cout << "程序结束" << std::endl;
    return 0;
}

看到什么算成功:

公司:出季度报表
HR:发出录用通知
HR:办离职手续
HR:发出录用通知
公司:出季度报表
公司:出季度报表
(这一段里 company 的名字都放行了)
程序结束

读结果三句话:

  1. 别名 hr 能像真名一样用(hr::fire()),而且能反复用——它是"换称呼",不是"放行名字"。
  2. using company::report; 放进了块里——出了块就失效(所以后面的 using namespace 那段还要再来一次)。
  3. 全篇用 std::cout 而没写一句 using namespace std;——照样跑得很顺。

30 秒实验:把 namespace hr = company::hr; 挪到 main 外面(文件级),然后在另一个函数里用 hr::fire()。你会发现也能用——因为别名的有效范围也遵守作用域规则:写在文件级就是全文件可用(这就是"全局别名")。反过来,把它写在某个块里,就只在那个块里有效。

六、常见误区

  1. "三种方式效果一样,随便挑"——效果确实一样(功能上),但风险差别很大。方式①最安全、方式③最省字也最危险。 选哪个是"规范"问题,不是"对不对"问题。
  2. "using namespace std; 写在文件开头是标准做法"——恰恰相反。它是教程为了排版简洁的写法,工程规范基本都禁止(尤其头文件里)。从今天起,试着把它从"文件开头"挪到"函数体里",或者干脆用全名。
  3. "using 声明和 using 指令是一回事"——不是。using std::cout;(声明)只放行一个名字;using namespace std;(指令)放行整个命名空间。前者风险小得多。
  4. "命名空间别名会"改变"名字的归属"——不会。fs::path 和 std::filesystem::path 是同一个东西,别名只是让你少打字。(这点和 typedef 8.2 很像:只是换个名字。)
  5. "using 可以写在命名空间里"——可以,但要注意:using 指令写在命名空间内部,会让那个命名空间"出口"变宽(别人包含它时更容易撞名)。这也是"头文件里别写 using namespace"这条规矩的一部分。
自测三问(点开对答案) 1. 三种使用方式各是什么?工程里首选哪一种?——① **写全名**(`std::cout`);② **`using` 声明**(`using std::cout;`,只放行一个名字);③ **`using namespace`**(整段放行)。**工程首选 ①**(最清楚、绝不撞名);**局部简化可用 ②**。 2. `using std::cout;` 和 `using namespace std;` 有什么区别?——**前者只放行 `cout` 这一个名字**(其他还得写 `std::`);**后者把 `std` 里所有名字都放出来**。**前者风险小得多。** 3. 命名空间别名怎么写?它和 `using` 有什么区别?——写法:**`namespace 别名 = 原名;`**(别名在前)。区别:**别名是"换个短称呼"**(还得写 `别名::名字`,**保留了归属信息**);**`using` 是"把名字放出来"**(可以直接裸写名字)。**长名字场景首选别名。**

相关笔记