【C++】编写自己的头文件以及extern与static关键字的用法和注意事项

一:编写自己的头文件

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 包含它们的定义。

  1. hepler.h 文件:
#ifndef HELPER_H // 防止头文件被多次包含
#define HELPER_H

// 在头文件中声明全局变量
extern int globalVariable;
// 在头文件中声明函数
void printMessage();

#endif
  1. 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;
}
  1. 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; // 显式初始化静态变量

  • 局部静态变量的线程安全性:如果多个线程同时访问同一个局部静态变量,可能会导致竞态条件。要确保线程安全性,可以使用适当的同步机制,如互斥锁。

  • 静态成员变量的定义:静态成员变量 需要在类内声明,外部进行定义和初始化。确保在实现文件中定义静态成员变量,以避免多重定义错误。

  • 静态成员函数的使用:静态成员函数不具有访问非静态成员变量或函数的权限。它们只能访问其他静态成员或静态数据。

  • 静态变量的生命周期:局部静态变量在程序的整个生命周期内保持其值。静态成员变量在程序执行期间只有一个副本,它们的生命周期也很长。


希望大家多多支持,码字不易,若有错误请指出,共同学习!
欢迎关注🔎点赞👍收藏⭐️留言📝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值