CPP 9. 内存模型和名称空间 2020-11-05 浏览量 557 暂无评论 # 第9章 内存模型和名称空间 ## 9.1 单独编译 一般将一个大型文件分解为三部分: - 头文件:包含结构声明和使用这些结构的函数的原型(通常有函数原型、使用#define或const定义的符号常量、结构声明、类声明、模板声明、内联函数,不能将函数定义放在头文件中,当同一个程序的两个文件都包含了这个头文件时,相当于包含了同个函数的两次定义,将出错); - 源代码文件:包含与结构有关的函数的代码; - 源代码文件:包含调用与结构相关的函数的代码。 🐸小知识: 1. 在包含自己的头文件时,应使用引号而不是尖括号(首先查找当前的工作目录或源代码目录,而不是在标准位置); **示例** ```cpp // coordin.h // 使用ifndef避免在同一个文件中包含同一个头文件多次 // (当包含的文件中也包含该头文件时会出现这种情况) #ifndef COORDIN_H_ #define COORDIN_H_ struct polar { double distance; double angle; }; struct rect { double x; double y; }; polar rect_to_polat(rect xypos); void show_polar(polar dapos); #endif // file1.cpp #include #include "coordin.h" using namespace std; int main() { rect rplace; polor pplace; cout << "Enter the x and y values: "; while (cin >> rplace.x >> rplace.y) { pplace = rect_to_polar(repalce); show_polar(pplace); cout << "Next two numbers (q to quit): "; } cout << "Bye!\n"; return 0; } // file2.cpp #include #include #include "coordin.h" polar rect_to_polar(rect xypos) { using namespace std; polar answer; answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y); answer.angle = atan2(xypos.y, xypos.x); return answer; } void show_polar(polar dapos) { using namespace std; const double Rad_to_deg = 57.29577951; cout << "distance = " << dapos.distance; cout << ", angle = " << dapos.angle * Rad_to_deg; cout << " degrees\n"; } ``` ## 9.2 存储持续性、作用域和链接性 **存储持续性:** 自动存储持续性、静态存储持续性、线程存储持续性(C++11)、动态存储持续性。 **作用域(scope):** 名称在文件的多大范围内可见,分为局部和全局(名称空间作用域的特例)。 **链接性(linkage):** 名称如何在不同文件间共享,分为无、外部和内部。 ```cpp int global = 1000; // 外部链接,程序的其他文件中也可以使用 static int one_file = 50; // 内部链接,只在该文件中可以使用; int main() { ... } void funct1(int n) { static int count = 0; // 无链接静态变量,只在该函数中能访问,但在程序整个运行期间都在内存中 int llama = 0; // 自动变量 ... } ``` 🐸小知识: 1. 默认情况下,函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性,存储在栈中,后进先出; 2. 关键字auto和register的演变历史(之前的用法现在不用了,但不非法); 3. 虽然无链接性的静态持续变量和自动变量都是只能在当前函数或代码块中访问,但是自动变量不会像静态持续变量那样在整个程序执行期间存在,适用于再生; 4. 单定义规则:如果要在多个文件中使用**外部变量**,只需在一个文件中包含该变量的定义,但在使用该变量的其他所有文件中,都必须使用关键字extern声明它; 5. 作用域解析运算符"::",放在变量名前面表示使用变量的全局版本; 6. 虽然使用全局变量可以不用传递参数,但这样的程序不可靠,通常情况下,应使用局部变量,外部存储适于表示常量数据; 7. const全局变量的链接性为内部; ```cpp // 第一个const防止字符串被修改,第二个字符串防止每个指针的位置被修改 const char * const months[12] = { "January", "February", "March", ..., "December" } ``` 8. 一个文件中与另一文件中常规外部变量的同名静态外部变量将隐藏常规外部变量; ### 9.2.1. 说明符和限定符 **a. c-v限定符** - const - volatile。表明即使程序代码没有对内存单元进行修改,其值也可能发生变化,比如串口通信中,硬件可能修改其中的内容。它的作用是改善编译器的优化能力,防止这种变量被错误优化。 **b. mutable** 用来指出即使结构(或类)变量为const,其某个成员也可以被修改。 ### 9.2.2. 函数的链接性 1. 默认情况下,函数的链接性为外部,可以在不同的文件中使用;还可以用关键字static将函数的链接性设置为内部的,在定义静态函数的文件中,静态函数将覆盖外部定义。 2. 单定义规则也适用于非内联函数,这意味着**在多文件程序中,只能有一个文件包含该函数的定义(该文件可能是库文件)**; 3. 内联函数的定义可以放在头文件中; 4. C++查找函数的顺序:(若静态)本文件-->所有程序文件-->库; ### 9.2.3. 语言链接性 C语言和C++编译器为函数名重命名的方式不同,因此在C++中想要使用C库文件中预编译的函数时,需要指定使用C语言链接性。 ```cpp extern "C" void spiff(int); // C extern void spoff(int); // C++ extern "C++" void spaff(int); // C++ ``` ## 9.3 名称空间 名称空间是为了解决大项目中的名称冲突而出现的。 ```cpp namespace A { double pail; void fetch(); } // 名称空间是开放的,即可以把名称加入到已有的名称空间中 namespace A { int haha; } namespace B { int pail; double featch; } // 名称空间可以嵌套 namespace C { namespace D { int a; ... } float water; } // 可以给名称空间创建别名 namespace ABC = A::B::C; // 限定的名称 A::haha b::pail // 如果不想每次使用都进行限定怎么办呢?两种办法 // 1. using声明,使特定的标识符可用 using A::haha; // 2. using编译指令,使整个名称空间可用 using namespace A; #include // 将名称加入名称空间std using namespace std; // 使名称全局可用 ``` ## 9.4 总结 尽量将变量、函数库、类库写在名称空间中,多方便,多优雅。 赞赏 微信支付 支付宝支付