avatar

目录
328 - cpp命名空间

C++命名空间

🧑‍💻 User

In this lecture, we’re going to see how we can create and use our own namespaces.And the namespace is really put in place to protect you from multiple definitions for a function.Let’s look at an example here.Here we have a few functions and the two functions are called Add and two functions are called for subtract.And we want to have all these functions in place.But one thing you should notice that is going to trip us up is that we have the same signature for thesame function name.If you look here, the Add function has a return type of double.It has two parameters of double type.And we have the same thing here.If we try to compile this code, we’re going to get a compiler error saying that we are redefining theAdd function here and this is going to be bad.So sometimes you want to have different definitions for the same function.For example, notice here we have different implementations for our Add function here.One is just going to add the parameters and the other is going to do some adjustment on the sum thatwe come up with here.And suppose this is something really useful for your project.If you want to do this, you’re going to get a compiler error and you’re not going to achieve your goal.Namespaces are a technique or a facility we have in C plus plus to allow us to do something like this.And the syntax to declare a namespace is really simple.You say namespace, you say the namespace name, and then you put a pair of curly braces inside thosecurly braces.You’re going to put whatever you want to constrain to that namespace.For example, the first namespace here is called No Weight, meaning that we’re not doing any adjustmenton the sum here.And we have a second one which is called Weight Inside.We’re going to put the second version of our Add function and we’re going to do our adjustment here.If we set up our code like this and try to compile the code, it is going to compile and work fine becausenow C plus plus is going to see that the first add function here is living in a different place thanthe second add function here.And the C plus plus compiler is going to accept this.We have the same setup for our sub functions here.We have a no weight namespace and we have a weight namespace and this is going to work.Another thing you can do with namespaces is use them to split your code across declarations and definitions.For example, here you see our namespace weight.It is going to be spanning different blocks.The first block is right here and the second block is right here.And the C plus plus is going to accept this.And the C plus plus compiler is going to do whatever magic it needs to do to keep track of this namespacein this file here.So we’re going to have our declarations on top here and we’re going to have our definitions down below.And this is going to work.Once we have the namespace set up, we can use it in the main function.And here is a simple example.For example, using things from the weight namespace, all you have to do is prefix whatever you wantto call with the namespace name and a pair of colons here and this is going to tell the compiler I wantto call the div function which lives in the weight namespace.Okay.So this is really it.This is how you set up your namespaces.You say namespace, you say the namespace name, you put a pair of curly braces and inside you put whateveryou want to leave in the namespace.To use the namespace you say the namespace name like you see here.You put a pair of colons and then you say the name of the function or the name of whatever it is youwant to access through this namespace here.Now that you know this, we’re going to head over to Visual Studio code and play with this a littlemore.Okay.So here we are in our working folder.The current project is creating namespaces.We’re going to grab our template files pretty quick and we’re going to put them in place and we aregoing to open this in Visual Studio code by dragging and dropping here, this is going to open our project.We have the main CPP file.We can clean it up a little bit.Let’s remove this and we close the left sidebar here.We are going to put in our starter code and it is nothing really complicated.We have a variable which is going to be our adjustment.We have the Add function which is going to just add the parameters we pass in here and return the result.And we have another Add function which is going to not only Add, but it’s also going to do an adjustmentand give us the result.But as you already know, if we try to compile this, this is not going to work because we have thesame function defined twice in our program.So for example, if we go down here and say add and say two and three, for example, and try to compilethis program, we’re going to pass this through GCC, you’re going to see world finished with errors.And that the problem is we are redefining.And the Add function here and C plus plus or C plus plus compilers are not going to allow this.So we really need a way to tell the compiler that these are different functions or functions for differentpurposes and we can use namespaces to do that.So for example, we can set up a namespace that says that the first function here has no adjustmentand that the second function has an adjustment.And we can say namespace and we can say the namespace name here, we can say adjust, we can save thisand then we’re going to take our function here.Or we can call this new adjust and we can grab our function here and put that in our namespace and wecan align this things a little bit better.And we’re going to do the same thing for the second function here.We’re going to move that in a separate namespace and the namespace is going to be called Adjust.Let’s do that.We’re going to say namespace and the namespace name is going to be adjust and we’re going to grab ourfunction here and move that in our namespace.Let’s do that and we’re going to move things a little bit to the right.And now the C plus plus compiler is not going to be confused if it sees these two functions, even ifthey have the same signature.The C plus plus compiler is going to say, Aha, this one lives in a different namespace than the otherone.So they are different.But we still have a problem.How do we call this functions here?Because if we try to compile this program without calling the function, you’re going to see that theworld is going to be good.But if we come here and say, Add two and three, for example, we’re going to get a compiler errorbecause the C plus plus compiler can’t figure out which version of the add function we want to call.Do we want to call the one from the no adjust namespace or do we want to call the one from the adjustnamespace?The C plus plus compiler doesn’t know C plus plus provides a syntax.We can use to say which namespace the name we want to use lives in, and all we have to do is say thenamespace name in front of your function.For example, we can say adjust and say Colon.Colon.This is going to tell the C plus plus compiler.Aha, this guy wants to call the add function living in the adjust namespace.And if we grab the result, for example, let’s say double result and print this out, we can say stdcoutresult.This is going to work just fine.Let’s build this and we’re going to pass this through GCC like we always do.The world is good, but now we can bring up a terminal and actually run this program and see it doingits magic.We can clear and run Rooster and we’re going to see that our sum is not five.It’s going to be adjusted because we are using the adjusted version of the Add function.If we want to do adjustment and if we want to get a five, we can use the adjust namespace.We can do that.And if we build now, the build is going to be good, but we can clear and run Rooster, we’re goingto see that result is five.This is really how you can declare a namespace, put stuff inside and then use that namespace like wesee here.Okay.Now that we have seen how to set up namespaces, I want you to see that it is also possible to splitthings across declarations and definitions using namespaces.So for example, we can set up our multiplication and division functions here.For example, we can go down and say namespace and say No, adjust and we’re going to set up anothernamespace and call that adjust.And this side we’re going to put our declarations for the multiplication and division functions so wecan go down and say double mult and we’re going to say double X and double Y because that’s what wehave been using all along for the other functions here.And we’re going to say double diff and we’re going to divide X and Y.Let’s do that.We can actually grab the same declarations here and use them in our adjust namespace because the signaturesfor the functions are really the same.The differences are going to come up when we do the actual definitions for the functions.Now that we have the declarations and let’s say that these are declarations, we can go down and putin the definitions for these functions here, we can grab the entire thing here and we’re going to adjustthis a little bit.We can go down below the main function or if we want, we can even put this in a separate translationUnit linkers are going to be smart enough to go and pick these things up wherever they live in our project.But we want to keep things simple in this lecture.So we’re going to put this below the main function here and we’re going to use them.So now we have these things and we want to put in the definitions.Let’s do that.We’re going to return.X multiplied by Y here and we’re going to return X divided by Y and we’re going to return the resultof dividing X by Y here.Let’s do that.And this should do it.We’re going to go down and do the versions that do adjustments to our results.We’re going to multiply X and Y and then do the adjustment.The adjustment is just going to be minus adjustment.We want to keep things simple here, so we’re going to use this and then we’re going to do the divisionand do our adjustment.We’re going to return X divided by Y, and then we’re going to do our adjustment here by subtractingour adjustment value.Let’s go back and see that we did divide correctly.This this looks really cool.So now we’re going to try and use our multiplication and division functions.If we change the no adjust here and use mult to multiply, let’s do that.This should multiply two and three and we should get the result in this variable here and print thisout.So we expect to get a sex because we are using the no adjust version of our multiplication functionhere.And if we go down, we’re going to see that this is just going to multiply X and Y and return the result.We can build this through GCC like we always do.You’re going to see the world is good.We can clear and run Rooster and we’re going to get a sex.If we go back in main and say adjust, we’re going to adjust the result of multiplying two and threeand we’re going to get something less than six because all we are really doing is subtracting the adjustmentfrom our multiplication here.And this is going to be what we see when we run the application here.We’re going to build with GCC.The build is good.We can clear and run Rooster.We’re going to get a 5.2 something and this is what we expect.We can even use the division function.For example, we can set a 12 and divide that by three here.And if we do the adjustment, we’re not going to get a four.We’re going to get something less than four because we are subtracting from that.Okay, so let’s build again, we’re going to pass this through GCC.The build is good.We can clear and run the rooster.We’re going to get 35 something.What is going on here?Let’s go back and see what we did in Main.We are actually multiplying.We’re not dividing.Let’s divide and we’re going to do the adjustment.So let’s run this through GCC.The build is good.We can clear and run Rooster.We’re going to get a three point something, which is what we expect if we don’t do any adjustment onour division and say, no, adjust.This is going to divide 12 by three and we’re going to get a four as the result and we’re going to printthis out.Let’s do this.We’re going to run this through GCC.The build is good.We can clear and run Rooster.We’re going to get a four.And this is really what we expect.This is really all I wanted you to see in this lecture, showing you how you can declare your own namespaces.The syntax is really simple.You say namespace, you say the namespace name, you put a pair of curly braces and inside those curlybraces you’re going to put whatever you think should live in your namespace.It is even possible to split things between declarations and definitions and really use your namespacehowever you see fit.We are going to stop here in this lecture and the next one we’re going to see how you can split yournamespace across different files.And we’re going to extend the idea here and use our namespaces to include class types.So go ahead and finish up here and meet me there.


🤖 Assistant

同学们好!今天我们将深入学习C++中一个非常重要的概念:命名空间 (Namespace)

标题:C++命名空间深度解析:告别命名冲突,构建清晰代码结构

摘要

本节课将详细讲解C++命名空间的作用、定义方式和使用方法。你将学会如何利用命名空间解决代码中的命名冲突 (Name Collision)问题,保护函数免受多重定义 (Multiple Definitions)的困扰,并学会如何在不同代码块甚至不同文件中组织命名空间,最终编写出更清晰、更易维护的C++代码。


2. 详细讲解:C++命名空间入门与精通

2.1 为什么我们需要命名空间?——理解命名冲突

在C++编程中,随着项目规模的扩大,我们可能会遇到一个常见的问题:命名冲突。想象一下,你和你的同事都在各自的代码模块中定义了一个名为 Add 的函数。如果你们都将这些函数放在同一个程序文件中,编译器就会感到困惑,因为它不知道当你调用 Add 时,究竟是想使用哪一个 Add 函数。

问题示例:
考虑以下代码片段:

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 场景一:尝试在同一个“全局空间”中定义两个同名函数
double Add(double a, double b) {
return a + b;
}

double Add(double a, double b) { // 编译错误!重复定义!
return a + b - 0.8; // 假设这个是需要进行调整的加法
}

// 另一个函数,同样可能遇到命名冲突
double Subtract(double a, double b) {
return a - b;
}

double Subtract(double a, double b) { // 编译错误!
return a - b + 0.5; // 假设这个是需要进行调整的减法
}

这段代码试图定义两个同名且函数签名 (Function Signature)完全相同的 Add 函数(返回类型和参数列表都一样)。当C++编译器遇到这种情况时,它会报错,提示 Add 函数被重新定义 (Redefining)`。这是因为在默认情况下,所有函数都处于一个全局命名空间 (Global Namespace)中,这个空间不允许出现同名的函数(除非它们是函数重载 (Function Overloading),即函数名相同但参数列表不同)。

但在实际开发中,我们可能确实需要不同版本的 Add 函数,它们做着类似但又有所差异的工作(比如一个简单相加,一个相加后还进行调整)。命名空间就是解决这个问题的利器 (Facility)

2.2 什么是命名空间?——给你的代码分区

命名空间本质上是一个声明区域 (Declarative Region)。它允许你将特定的函数、类、变量等组织起来,形成一个逻辑上的“区域”或“容器”。这样,即使不同区域内存在同名的实体,它们也不会相互冲突,因为它们“居住”在不同的命名空间中。你可以把它想象成不同国家(命名空间)都有同名的城市(函数名),但因为在不同的国家,它们彼此不会混淆。

2.3 如何定义命名空间?——语法介绍

定义命名空间的语法非常简单:

cpp
1
2
3
namespace 命名空间名称 {
// 在这里放入你的函数、类、变量等
}
  • namespace 是关键字。
  • 命名空间名称:是你为这个命名空间起的唯一名字。建议使用有意义的名称,例如 NoAdjust (无调整) 或 Adjust (有调整)。
  • { }:一对花括号,用于包裹属于该命名空间的所有声明和定义。

示例:解决上述问题

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 定义第一个命名空间:无调整的加法和减法
namespace NoAdjust {
double Add(double a, double b) {
return a + b;
}

double Subtract(double a, double b) {
return a - b;
}
}

// 定义第二个命名空间:有调整的加法和减法
namespace Adjust {
// 假设有一个私有的调整值,这是命名空间内部的变量
const double adjustment_value = 0.8;

double Add(double a, double b) {
return a + b - adjustment_value;
}

double Subtract(double a, double b) {
return a - b + adjustment_value;
}
}

现在,NoAdjust::AddAdjust::Add 是两个完全不同的函数,C++编译器可以明确地区分它们,不再会报错。

2.4 如何使用命名空间中的成员?——作用域解析运算符 (Scope Resolution Operator)

要访问命名空间内部的成员(函数、变量、类等),你需要使用作用域解析运算符 (Scope Resolution Operator),即 ::

语法:
命名空间名称::成员名称

示例:在 main 函数中使用

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream> // 使用标准库的cout,它也在std命名空间中

// 上面定义的 NoAdjust 和 Adjust 命名空间... (此处省略,假设已定义)

int main() {
// 调用 NoAdjust 命名空间中的 Add 函数
double result1 = NoAdjust::Add(5.0, 3.0);
std::cout << "无调整的加法结果: " << result1 << std::endl; // 预计输出 8.0

// 调用 Adjust 命名空间中的 Add 函数
double result2 = Adjust::Add(5.0, 3.0);
std::cout << "有调整的加法结果: " << result2 << std::endl; // 预计输出 8.0 - 0.8 = 7.2

// 调用 NoAdjust 命名空间中的 Subtract 函数
double result3 = NoAdjust::Subtract(10.0, 4.0);
std::cout << "无调整的减法结果: " << result3 << std::endl; // 预计输出 6.0

// 调用 Adjust 命名空间中的 Subtract 函数
double result4 = Adjust::Subtract(10.0, 4.0);
std::cout << "有调整的减法结果: " << result4 << std::endl; // 预计输出 6.0 + 0.8 = 6.8

return 0;
}

通过 NoAdjust::AddAdjust::Add 这种明确的指定,我们告诉编译器我们想要调用哪个版本的 Add 函数。

2.5 命名空间的拆分与组织——声明与定义的分离

命名空间的一个强大特性是,你可以在多个不连续的块中定义同一个命名空间。这意味着你可以将命名空间的声明 (Declarations)放在一个地方(例如头文件),而将定义 (Definitions)放在另一个地方(例如源文件),这与组织普通函数的方式非常相似。

当编译器看到同一个名称的 namespace 块时,它会将这些块中的所有成员都合并到同一个逻辑命名空间中。

示例:拆分声明和定义

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// --- 头文件 (例如: operations.h) ---
#pragma once // 防止头文件被多次包含

// 声明 NoAdjust 命名空间的一部分
namespace NoAdjust {
double Multiply(double x, double y); // 声明乘法函数
double Divide(double x, double y); // 声明除法函数
}

// 声明 Adjust 命名空间的一部分
namespace Adjust {
double Multiply(double x, double y); // 声明乘法函数
double Divide(double x, double y); // 声明除法函数
}

// --- 源文件 (例如: operations.cpp) ---
#include "operations.h" // 包含声明

// 接着在同一个源文件中,可以定义之前声明的函数
// 重要的是,你可以在不同的 namespace 块中继续定义
namespace NoAdjust {
double Multiply(double x, double y) {
return x * y;
}
} // 结束 NoAdjust 命名空间的一个定义块

namespace NoAdjust { // 可以再次打开同一个命名空间来添加更多定义或声明
double Divide(double x, double y) {
if (y == 0) {
// 实际项目中需要更完善的错误处理
return 0.0;
}
return x / y;
}
} // 结束 NoAdjust 命名空间的另一个定义块

// 定义 Adjust 命名空间中的函数
namespace Adjust {
const double adjustment_value_mul_div = 0.5; // 可以有不同的调整值

double Multiply(double x, double y) {
return x * y - adjustment_value_mul_div;
}

double Divide(double x, double y) {
if (y == 0) {
// 实际项目中需要更完善的错误处理
return 0.0;
}
return x / y - adjustment_value_mul_div;
}
} // 结束 Adjust 命名空间定义块

这种分离声明和定义的方式是大型项目中的常见实践,有助于提高代码的模块化和可维护性。

2.6 标准库中的命名空间——std

你可能已经注意到,我们经常使用 std::coutstd::endl。这里的 std 就是C++标准库(Standard Library)的所有内容所处的命名空间。这进一步证明了命名空间在实际项目中的重要性。如果没有 std 命名空间,coutendl 等名称会与你自己的代码或其他库中的名称发生冲突。

3. 代码示例:完整演示

下面是一个完整的C++程序,演示了命名空间的定义、拆分以及使用。

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <iostream> // 引入标准输入输出流,位于 std 命名空间

// --- 声明部分:通常放在头文件中 ---

// 定义 NoAdjust 命名空间
namespace NoAdjust {
// 声明无调整的加法和减法函数
double Add(double a, double b);
double Subtract(double a, double b);

// 声明无调整的乘法和除法函数
double Multiply(double x, double y);
double Divide(double x, double y);
}

// 定义 Adjust 命名空间
namespace Adjust {
// 声明有调整的加法和减法函数
double Add(double a, double b);
double Subtract(double a, double b);

// 声明有调整的乘法和除法函数
double Multiply(double x, double y);
double Divide(double x, double y);
}

// --- 定义部分:通常放在源文件中 ---

// NoAdjust 命名空间的定义
namespace NoAdjust {
double Add(double a, double b) {
return a + b;
}

double Subtract(double a, double b) {
return a - b;
}
} // NoAdjust 命名空间定义块结束

// 可以在另一个 NoAdjust 命名空间块中继续定义
namespace NoAdjust {
double Multiply(double x, double y) {
return x * y;
}

double Divide(double x, double y) {
if (y == 0) {
std::cerr << "Error: Division by zero in NoAdjust::Divide!" << std::endl;
return 0.0; // 错误处理,返回0.0
}
return x / y;
}
} // NoAdjust 命名空间定义块结束

// Adjust 命名空间的定义
namespace Adjust {
const double GLOBAL_ADJUSTMENT = 0.7; // 定义一个常量调整值,只在该命名空间可见

double Add(double a, double b) {
return a + b - GLOBAL_ADJUSTMENT;
}

double Subtract(double a, double b) {
return a - b + GLOBAL_ADJUSTMENT;
}
} // Adjust 命名空间定义块结束

// 可以在另一个 Adjust 命名空间块中继续定义
namespace Adjust {
// 注意:这里的 GLOBAL_ADJUSTMENT 依然是上面定义的那个,因为它们属于同一个 Adjust 命名空间
double Multiply(double x, double y) {
return x * y - GLOBAL_ADJUSTMENT;
}

double Divide(double x, double y) {
if (y == 0) {
std::cerr << "Error: Division by zero in Adjust::Divide!" << std::endl;
return 0.0; // 错误处理
}
return x / y - GLOBAL_ADJUSTMENT;
}
} // Adjust 命名空间定义块结束


// --- 主函数:程序入口 ---
int main() {
double val1 = 10.0;
double val2 = 5.0;
double result;

// 使用 NoAdjust 命名空间中的函数
std::cout << "--- 使用 NoAdjust 命名空间 ---" << std::endl;
result = NoAdjust::Add(val1, val2);
std::cout << val1 << " + " << val2 << " (无调整) = " << result << std::endl; // 15.0

result = NoAdjust::Subtract(val1, val2);
std::cout << val1 << " - " << val2 << " (无调整) = " << result << std::endl; // 5.0

result = NoAdjust::Multiply(val1, val2);
std::cout << val1 << " * " << val2 << " (无调整) = " << result << std::endl; // 50.0

result = NoAdjust::Divide(val1, val2);
std::cout << val1 << " / " << val2 << " (无调整) = " << result << std::endl; // 2.0

std::cout << "\n--- 使用 Adjust 命名空间 ---" << std::endl;
// 使用 Adjust 命名空间中的函数
result = Adjust::Add(val1, val2);
std::cout << val1 << " + " << val2 << " (有调整) = " << result << std::endl; // 15.0 - 0.7 = 14.3

result = Adjust::Subtract(val1, val2);
std::cout << val1 << " - " << val2 << " (有调整) = " << result << std::endl; // 5.0 + 0.7 = 5.7

result = Adjust::Multiply(val1, val2);
std::cout << val1 << " * " << val2 << " (有调整) = " << result << std::endl; // 50.0 - 0.7 = 49.3

result = Adjust::Divide(val1, val2);
std::cout << val1 << " / " << val2 << " (有调整) = " << result << std::endl; // 2.0 - 0.7 = 1.3

// 尝试除以零的情况
std::cout << "\n--- 测试除零错误处理 ---" << std::endl;
Adjust::Divide(val1, 0.0); // 会打印错误信息并返回 0.0

return 0;
}

4. 问答卡片 (QA Flash Cards)

  1. Q1: 命名空间 (Namespace) 在C++中主要用来解决什么问题?

    • A1: 解决命名冲突 (Name Collision)问题,当不同模块或作者定义了相同名称的函数、类、变量时,通过将它们置于不同的命名空间中,避免编译器混淆和多重定义 (Multiple Definitions)错误。
  2. Q2: 如何定义一个命名空间?请写出基本语法。

    • A2: 使用 namespace 关键字,后跟命名空间名称,然后是一对花括号 {} 包裹其成员。
      namespace MySpace { // 成员 }
  3. Q3: 如果我定义了 namespace MyMath { double Add(double a, double b); },在 main 函数中如何调用这个 Add 函数?

    • A3: 使用作用域解析运算符 (Scope Resolution Operator) ::
      double sum = MyMath::Add(10.0, 20.0);
  4. Q4: 命名空间中的声明和定义可以分开吗?如果可以,如何实现?

    • A4: 可以。你可以在一个 namespace 块中声明成员(例如在头文件),然后在另一个(或多个)同样名称的 namespace 块中定义这些成员(例如在源文件)。编译器会将所有同名命名空间块中的内容合并为一个逻辑命名空间。
  5. Q5: std::cout 中的 std 是什么?

    • A5: std 是C++标准库 (Standard Library)的命名空间。C++标准库中的所有函数、类、对象等都位于 std 命名空间下,以避免与用户或其他库的命名冲突。

5. 常见误解或新手错误 (Common Misunderstandings/Mistakes)

  1. 忘记使用作用域解析运算符 :: 新手常常在定义了命名空间后,直接调用其中的函数,例如 Add(a, b); 而不是 NoAdjust::Add(a, b);。这会导致编译器报错,提示找不到 Add 函数,因为它默认查找的是当前作用域或全局命名空间。
  2. 过度使用 using namespace std; 尽管 using namespace <name>; 可以方便地让你直接使用命名空间中的成员而无需前缀,但在头文件或大型项目的源文件中过度使用它(特别是 using namespace std;)是一个坏习惯。它会引入该命名空间的所有名称到当前作用域,可能导致新的命名冲突,尤其是在项目引入多个库时。最佳实践是显式地 (Explicitly)使用 std:: 或只针对特定名称使用 using std::cout;。在本课程中,我们主要演示显式调用,以培养良好习惯。
  3. 误解命名空间是“文件范围”的: 有些新手可能认为一个 namespace 块只能在一个文件里定义一次。实际上,同一个命名空间可以在不同的文件或同一个文件中的多个不连续的代码块中被打开和扩展。编译器会把所有这些同名的块合并成一个命名空间。
  4. 在命名空间内部再次定义同名函数: 即使使用了命名空间,你也不能在同一个命名空间内部定义两个函数签名 (Function Signature)完全相同的函数。命名空间解决的是不同命名空间之间的命名冲突,而不是同一命名空间内部的冲突。例如:
    cpp
    1
    2
    3
    4
    namespace MySpace {
    double func();
    double func(); // 错误:在MySpace内部重复定义
    }

6. 编程练习 (Coding Exercise)

任务:
你将创建一个简单的计算器模块。这个模块需要提供两种模式的加法和减法功能:一种是标准模式 (Standard Mode),直接进行加减;另一种是日志模式 (Logging Mode),在进行加减运算的同时,还会打印一条日志信息。

请你完成以下代码中的空白部分,实现这个功能。

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>

// --- 声明部分 ---

// 定义 Standard 命名空间,用于标准模式的加法和减法
namespace Standard {
// TODO 1: 声明 Standard 命名空间下的 Add 函数
double Add(double a, double b);

// TODO 2: 声明 Standard 命名空间下的 Subtract 函数
double Subtract(double a, double b);
}

// 定义 Logging 命名空间,用于日志模式的加法和减法
namespace Logging {
// TODO 3: 声明 Logging 命名空间下的 Add 函数
double Add(double a, double b);

// TODO 4: 声明 Logging 命名空间下的 Subtract 函数
double Subtract(double a, double b);
}

// --- 定义部分 ---

namespace Standard {
// TODO 5: 实现 Standard 命名空间下的 Add 函数
// 它应该返回两个数的和
double Add(double a, double b) {
// ...
}

// TODO 6: 实现 Standard 命名空间下的 Subtract 函数
// 它应该返回两个数的差
double Subtract(double a, double b) {
// ...
}
} // Standard 命名空间定义结束

namespace Logging {
// TODO 7: 实现 Logging 命名空间下的 Add 函数
// 它应该先打印一条日志信息 "Performing logged addition...",然后返回两个数的和
double Add(double a, double b) {
// ...
return a + b;
}

// TODO 8: 实现 Logging 命名空间下的 Subtract 函数
// 它应该先打印一条日志信息 "Performing logged subtraction...",然后返回两个数的差
double Subtract(double a, double b) {
// ...
return a - b;
}
} // Logging 命名空间定义结束

// --- 主函数:程序入口 ---
int main() {
double x = 15.0;
double y = 7.0;
double result;

// 使用 Standard 命名空间
std::cout << "--- Standard Mode ---" << std::endl;
result = Standard::Add(x, y);
std::cout << x << " + " << y << " = " << result << std::endl; // 期望输出 22.0

result = Standard::Subtract(x, y);
std::cout << x << " - " << y << " = " << result << std::endl; // 期望输出 8.0

std::cout << "\n--- Logging Mode ---" << std::endl;
// 使用 Logging 命名空间
result = Logging::Add(x, y);
std::cout << x << " + " << y << " = " << result << std::endl; // 期望先输出日志,然后输出 22.0

result = Logging::Subtract(x, y);
std::cout << x << " - " << y << " = " << result << std::endl; // 期望先输出日志,然后输出 8.0

return 0;
}

请完成以上 TODO 部分的代码,然后我会对你的解决方案进行评分和解释!


评论