目录
一:编写自己的头文件
1. 为什么要有头文件?
因为类一般都不定义在函数体内。当类定义在函数体外时,又多个文件需要这个类的定义,那么就需要保持一致。所以类通常被定义在头文件中,而且类所在的头文件名字应与类的名字一致。头文件通常包含那些只能被定义一次的实体,如类,const 和 constexpr 变量。
2. 头文件的编写方式
- 为确保头文件多次包含仍能安全工作的常用技术是 预处理器preprocessor)。预处理器是在编译之前执行的一段程序,可以部分地改变我们所写的程序。常用的预处理功能是 #include。
- 这其中还会用到的一项预处理功能是头文件保护符(header guard),头文件保护符依赖于预处理变量。预处理变量有两种状态:已定义和未定义。#define 指令把一个名字设定为预处理变量,另外两个指令则分别检查某个指定的预处理变量是否已经定义: #ifdef 当且仅当变量已定义时为真,#ifndef 当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直至遇到 #endif 指令为止。
// 格式
#ifndef MY_HEAD
#define MY_HEAD
//定义类 const 和 constexpr 变量等只能定义一次的实体
#endif
整个程序中的预处理变量包括头文件保护符必须唯一,通常的做法是基于头文件中类的名字来构建保护符的名字,以确保其唯一性。为了避免与程序中的其他实体发生名字冲突,一般把预处理变量的名字全部大写。
二:extern 关键字的使用
1. 基本概念:
extern 关键字用于声明一个全局变量,它的作用是告诉编译器该变量在其他文件中定义。通常,extern 声明放在头文件中,而变量的实际定义放在一个源文件中。这样,多个源文件就可以共享同一个全局变量。
2. 案例演示:
演示如何使用 extern 在分文件编写中共享全局变量和函数:假设有三个源文件:main.c、helper.c 和 helper.h,其中 helper.h 包含函数和变量的声明,而 main.c 和 helper.c 包含它们的定义。
- hepler.h 文件:
#ifndef HELPER_H // 防止头文件被多次包含
#define HELPER_H
// 在头文件中声明全局变量
extern int globalVariable;
// 在头文件中声明函数
void printMessage();
#endif
- helper.c 文件:
#include <iostream>
#include "helper.h" // 引入helper.h头文件
using namespace std;
// 在helper.c中定义全局变量
int globalVariable = 42;
// 在helper.c中定义一个函数
void printMessage() {
cout<<"this is printMessage"<<endl;
}
- main.c 文件:
#include <iostream>
#include "helper.h" // 引入 helper.h头文件
using namespace std;
int main() {
// 调用定义在 helper.c中的函数
printMessage();
// 使用 extern 声明的全局变量
extern int globalVariable;
cout<<"Value of globalVariable: %d "<<globalVariable<<endl;
return 0;
}
- 在示例中的main.c文件中,我们使用了 extern int globalVariable;来告诉编译器,globalVariable 是在其他源文件(在这里是helper.c)中定义的全局变量。这是编译器需要知道的信息,以便在编译 main.c 时,它可以生成正确的代码,以引用 helper.c 中定义的全局变量。
- 如果没有这个 extern 声明,编译器会认为 globalVariable 是在当前源文件中定义的,并且在链接时找不到 globalVariable 的定义,将导致链接错误。通过添加 extern 声明,编译器知道要在其他源文件中查找该变量的定义。
3. 注意事项:
-
避免过度使用: 避免过度使用extern。虽然它在某些情况下非常有用,但如果滥用它,可能会导致代码的可读性下降。只在必要的情况下使用extern来引用其他源文件中的变量或函数。
-
头文件保护: 在头文件中使用预处理器指令来避免多次包含(使用 #ifndef, #define, #endif 等),以防止重复定义extern声明。
-
初始化: extern变量不会分配内存空间,因此它们需要在其他源文件中进行实际的定义和初始化。这意味着在使用extern变量之前,必须确保它们已经被定义和初始化。否则,可能会导致未定义的行为。
-
注释文档: 对于特殊情况或复杂的代码,使用注释文档来解释为什么需要extern声明以及如何正确使用它们。
三:static 关键字的使用
1. 静态变量(Static Variables):
- 局部静态变量(Static Local Variables):在函数内部声明的变量,但其生命周期跨越函数调用。它们只会被初始化一次,通常用于在函数调用之间保持状态信息。
void someFunction() {
static int count = 0; // 静态局部变量
count++;
cout << "Count: " << count << endl;
}
- 静态全局变量(Static Global Variables):在全局作用域内声明的变量,但其链接性为内部链接(internal linkage),只能在当前源文件中访问。其他源文件无法访问它们。
static int globalVariable = 42; // 静态全局变量
2. 静态成员变量和静态成员函数(Static Member Variables and Functions):
- 静态成员变量(Static Member Variables):在类中使用static关键字声明的成员变量被称为静态成员变量。它们与类的实例无关,只有一个副本,所有类的实例共享它。通常用于存储与类相关的全局信息或状态。
class MyClass {
public:
static int staticVariable; // 静态成员变量声明
};
// 在类外初始化静态成员变量
int MyClass::staticVariable = 0;
- 静态成员函数(Static Member Functions):在类中使用static关键字声明的成员函数是静态成员函数。它们不与类的实例关联,可以直接通过类名调用,而不需要创建类的实例。
class MyClass {
public:
static void staticFunction() {
// 静态成员函数的实现
}
};
3. 命名空间中的静态变量和函数(Static Variables and Functions in a Namespace):
- 命名空间中的静态变量(Static Variables in a Namespace):在命名空间内使用static关键字可以限制变量的作用域,使其 仅在当前文件中可见,防止全局污染。
namespace MyNamespace {
static int staticVariable = 42; // 命名空间中的静态变量
}
- 命名空间中的静态函数(Static Functions in a Namespace):在命名空间内使用static关键字可以限制函数的作用域,使其 仅在当前文件中可见。
namespace MyNamespace {
static void staticFunction() {
// 命名空间中的静态函数
}
}
4. 注意事项:
-
初始化静态变量:静态变量需要显式初始化。局部静态变量在首次使用之前会被初始化,但最好在声明时明确初始化,以避免潜在的问题。
static int count = 0; // 显式初始化静态变量
-
局部静态变量的线程安全性:如果多个线程同时访问同一个局部静态变量,可能会导致竞态条件。要确保线程安全性,可以使用适当的同步机制,如互斥锁。
-
静态成员变量的定义:静态成员变量 需要在类内声明,外部进行定义和初始化。确保在实现文件中定义静态成员变量,以避免多重定义错误。
-
静态成员函数的使用:静态成员函数不具有访问非静态成员变量或函数的权限。它们只能访问其他静态成员或静态数据。
-
静态变量的生命周期:局部静态变量在程序的整个生命周期内保持其值。静态成员变量在程序执行期间只有一个副本,它们的生命周期也很长。
希望大家多多支持,码字不易,若有错误请指出,共同学习!
欢迎关注🔎点赞👍收藏⭐️留言📝