【c++】inline、extern和static

server/2024/12/19 17:57:37/

C++ 中 inlineexternstatic 的全面使用详解

在C++编程中,inlineexternstatic 是三个重要的关键字,分别用于控制函数和变量的链接性、内联展开以及存储持续性和作用域。本文将通过详尽的表格和丰富的案例,深入探讨这三个关键字的各类使用场景和注意事项。

关键字概述

关键字作用使用场景链接性内存分配
inline建议编译器将函数或变量进行内联展开,减少函数调用开销;支持inline变量小型频繁调用的函数,C++17及以后版本的内联变量对函数:默认为inline(多定义在头文件);对变量:内联变量具有外部链接自动存储持续性(函数内联);静态存储持续性(内联变量)
extern声明变量或函数在其他文件中定义,表示外部链接;用于变量的多重声明跨文件访问变量或函数,全局变量的多文件共享外部链接根据变量类型决定
static控制变量或函数的作用域和生命周期;限制可见性限制变量或函数的可见性,防止命名冲突;静态变量保持其值对函数和全局变量:内部链接;对局部变量:静态存储持续性静态存储持续性

详细说明及案例

1. inline

作用

  • 对函数:inline 建议编译器将函数调用替换为函数体本身,以减少函数调用的开销。适用于小型且频繁调用的函数。
  • 对变量(C++17及以后版本):inline 允许在多个翻译单元中定义同一个变量,避免多重定义错误。

使用场景

  • 函数:通常在头文件中定义,避免多重定义错误。
  • 变量:需要在多个文件中共享且只需要一个实例。
1.1 inline 修饰函数

示例

// utils.h
#ifndef UTILS_H
#define UTILS_Hinline int add(int a, int b) {return a + b;
}#endif // UTILS_H
// main.cpp
#include <iostream>
#include "utils.h"int main() {std::cout << "3 + 4 = " << add(3, 4) << std::endl;return 0;
}

说明

  • 以往都是在头文件中声明函数,在一个源文件中定义,而这种inline直接在头文件定义的方式适合使用频繁的小型函数,编译器会优化效率。被称为 内联优化
  • 不用inline的正常实现
// utils.h
#ifndef UTILS_H
#define UTILS_Hint add(int a, int b);  // 只声明函数#endif // UTILS_H// -------------------------// utils.cpp
#include "utils.h"int add(int a, int b) {  // 在源文件中定义函数return a + b;
}// -------------------------// main.cpp
#include <iostream>
#include "utils.h"int main() {std::cout << add(3, 4);
}

add函数定义为inline,允许在多个文件中包含该头文件而不会导致重复定义错误。

1.2 inline 修饰变量(C++17及以后)

示例

// config.h
#ifndef CONFIG_H
#define CONFIG_Hinline int globalConfig = 42; // 内联变量#endif // CONFIG_H
// main.cpp
#include <iostream>
#include "config.h"int main() {std::cout << "Global Config: " << globalConfig << std::endl;globalConfig++;std::cout << "Global Config after increment: " << globalConfig << std::endl;return 0;
}
// other.cpp
#include <iostream>
#include "config.h"void printConfig() {std::cout << "Global Config from other.cpp: " << globalConfig << std::endl;
}

说明
inline变量允许在多个文件中包含并定义同一个变量,编译器会确保只有一个实例。适用于配置变量或常量。


使用 inline 变量共享全局常量

  • 编译器支持 C++17,最好将全局变量定义为 inline constexpr 类型并放置在头文件中

constants.h

#ifndef DEMO1_CONSTANTS_H    
#define DEMO1_CONSTANTS_H    // c++17    
namespace constants {    inline constexpr double pi {3.14};    inline constexpr double avogadro {6.022};    inline constexpr double myGravity {9.2};    
}
#endif //DEMO1_CONSTANTS_H    

main.cpp

#include <iostream>
#include "constants.h"int main() {std::cout << constants::pi << '\n';return 0;
}

2. extern

作用
extern关键字用于声明变量或函数在其他文件中定义,表示这些变量或函数具有外部链接性。它允许跨多个文件共享变量或函数。

使用场景
在多个源文件中共享全局变量或函数。

2.1 extern 声明变量

示例

// config.h
#ifndef CONFIG_H
#define CONFIG_Hextern int globalCounter; // 声明外部变量void incrementCounter();  // 声明函数#endif // CONFIG_H
// config.cpp
#include "config.h"int globalCounter = 0; // 定义外部变量void incrementCounter() {globalCounter++;
}
// main.cpp
#include <iostream>
#include "config.h"int main() {std::cout << "Initial Counter: " << globalCounter << std::endl;incrementCounter();std::cout << "Counter after increment: " << globalCounter << std::endl;return 0;
}

说明
extern用于在config.h中声明globalCounter变量,使其在多个文件中可见,但实际定义在config.cpp中。

2.2 extern 声明函数

示例

// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_Hextern double multiply(double a, double b); // 声明外部函数#endif // MATH_UTILS_H
// math_utils.cpp
#include "math_utils.h"double multiply(double a, double b) {return a * b;
}
// main.cpp
#include <iostream>
#include "math_utils.h"int main() {double result = multiply(3.5, 2.0);std::cout << "3.5 * 2.0 = " << result << std::endl;return 0;
}

说明
虽然函数默认具有extern链接性,但显式使用extern可以提高代码可读性。上述示例展示了如何在多个文件中声明和定义函数以实现跨文件调用。

3. static

作用
static关键字有多种用途,主要用于控制变量或函数的作用域和生命周期。

  • 对于函数和全局变量:限制其在定义文件中的可见性,仅在当前文件内可见(内部链接)。
  • 对于局部变量:在函数内声明的静态变量具有静态存储持续性,函数调用结束后仍保持其值。

使用场景

  • 限制全局变量或函数的可见性,防止命名冲突。
  • 在函数内维护状态。
3.1 限制函数可见性(内部链接)

示例

// helper.cpp
#include <iostream>static void helperFunction() { // 仅在 helper.cpp 内可见std::cout << "Helper Function Called" << std::endl;
}void callHelper() {helperFunction();
}
// helper.h
#ifndef HELPER_H
#define HELPER_Hvoid callHelper();#endif // HELPER_H
// main.cpp
#include "helper.h"int main() {callHelper();// helperFunction(); // 错误:helperFunction 在此不可见return 0;
}

说明
helperFunction被声明为static,限制其仅在helper.cpp文件内可见,防止其他文件访问或命名冲突。

3.2 限制全局变量可见性(内部链接)

示例

// data.cpp
#include <string>static std::string internalData = "Secret Data"; // 仅在 data.cpp 内可见std::string getData() {return internalData;
}
// data.h
#ifndef DATA_H
#define DATA_Hstd::string getData();#endif // DATA_H
// main.cpp
#include <iostream>
#include "data.h"// extern std::string internalData; // 错误:internalData 是静态的int main() {std::cout << "Data: " << getData() << std::endl;return 0;
}

说明
internalData被声明为static,使其仅在data.cpp文件内可见,外部文件无法访问或修改该变量。

3.3 函数内的静态变量

示例

// counter.h
#ifndef COUNTER_H
#define COUNTER_Hint getNextCounter();#endif // COUNTER_H
// counter.cpp
#include "counter.h"int getNextCounter() {static int counter = 0; // 静态变量,保持其值return ++counter;
}
// main.cpp
#include <iostream>
#include "counter.h"int main() {std::cout << getNextCounter() << std::endl; // 输出 1std::cout << getNextCounter() << std::endl; // 输出 2std::cout << getNextCounter() << std::endl; // 输出 3return 0;
}

说明
counter变量在getNextCounter函数内被声明为static,使其在多次函数调用中保持其值,实现计数器的功能。

关键字详细比较

inline vs static vs extern

特性inline (函数)static (函数)extern (变量/函数)
链接性inline 函数具有inline链接性,允许多重定义但需相同实现static 函数具有内部链接,仅在定义文件内可见extern 声明具有外部链接,定义在其他文件中
用途提高小函数的执行效率,避免函数调用开销隐藏函数实现,防止其他文件访问跨文件共享全局变量或函数
定义位置通常在头文件中定义,允许在多个文件中包含在源文件中定义,避免在头文件中使用通常在头文件中声明,在源文件中定义
多重定义允许但必须相同,实现代码复用不允许在多个文件中定义相同名称的static函数或变量不允许在多个文件中定义同名的非inline变量或函数

使用注意事项

  1. inline 函数

    • 适用于短小、频繁调用的函数,避免函数调用的开销。
    • 定义在头文件中,避免多次定义错误。
    • 编译器可能忽略inline建议,根据优化策略决定是否内联。
  2. inline 变量(C++17及以后):

    • 允许在多个文件中定义同一个变量,但必须使用inline关键字。
    • 适用于配置常量或全局变量的共享。
  3. extern 变量

    • 在头文件中使用extern声明,全局定义在一个源文件中。
    • 避免在头文件中定义变量,防止重复定义。
  4. static 全局变量和函数

    • 限制其仅在定义文件中的可见性,防止外部访问和命名冲突。
    • 适用于实现文件内的辅助功能或数据。
  5. static 局部变量

    • 在函数内部声明,保持变量值在多次函数调用间的持续性。
    • 适用于需要记忆函数调用状态的场景,例如计数器。

综合案例

以下案例综合应用了inlineexternstatic,展示了它们在实际项目中的使用方式。

项目结构

project/
├── include/
│   ├── utils.h
│   ├── config.h
│   ├── helper.h
│   └── counter.h
├── src/
│   ├── main.cpp
│   ├── utils.cpp
│   ├── config.cpp
│   ├── helper.cpp
│   └── counter.cpp

文件内容

1. include/utils.h
// utils.h
#ifndef UTILS_H
#define UTILS_Hinline int add(int a, int b) {return a + b;
}#endif // UTILS_H
2. include/config.h
// config.h
#ifndef CONFIG_H
#define CONFIG_Hextern int globalCounter; // 声明外部变量inline int multiply(int a, int b) { // 内联函数return a * b;
}#endif // CONFIG_H
3. include/helper.h
// helper.h
#ifndef HELPER_H
#define HELPER_Hvoid callHelper();#endif // HELPER_H
4. include/counter.h
// counter.h
#ifndef COUNTER_H
#define COUNTER_Hint getNextCounter();#endif // COUNTER_H
5. src/utils.cpp
// utils.cpp
#include "utils.h"// 可以包含其他实现内容
6. src/config.cpp
// config.cpp
#include "config.h"int globalCounter = 0; // 定义外部变量void incrementCounter() {globalCounter++;
}
7. src/helper.cpp
// helper.cpp
#include <iostream>
#include "helper.h"static void helperFunction() { // 仅在 helper.cpp 内可见std::cout << "Helper Function Called" << std::endl;
}void callHelper() {helperFunction();
}
8. src/counter.cpp
// counter.cpp
#include "counter.h"int getNextCounter() {static int counter = 0; // 静态变量,保持其值return ++counter;
}
9. src/main.cpp
// main.cpp
#include <iostream>
#include "utils.h"
#include "config.h"
#include "helper.h"
#include "counter.h"int main() {// 使用 inline 函数std::cout << "3 + 4 = " << add(3, 4) << std::endl;// 使用 extern 变量和函数std::cout << "Initial Counter: " << globalCounter << std::endl;incrementCounter();std::cout << "Counter after increment: " << globalCounter << std::endl;// 使用 static 限制函数可见性callHelper();// 使用静态局部变量std::cout << "Next Counter: " << getNextCounter() << std::endl;std::cout << "Next Counter: " << getNextCounter() << std::endl;// 使用 inline 函数std::cout << "3 * 5 = " << multiply(3, 5) << std::endl;return 0;
}

运行结果

3 + 4 = 7
Initial Counter: 0
Counter after increment: 1
Helper Function Called
Next Counter: 1
Next Counter: 2
3 * 5 = 15

说明
此综合案例展示了如何在一个项目中合理使用inlineexternstaticinline用于定义小函数和内联变量,extern用于跨文件共享变量,static用于限制函数和变量的可见性以及保持局部变量的状态。

总结

  • inline

    • 函数:用于提示编译器将函数内联,适用于小型、频繁调用的函数,通常在头文件中定义。
    • 变量(C++17及以后):允许在多个文件中定义同一个变量,适用于配置变量或常量。
  • extern

    • 用于声明在其他文件中定义的变量或函数,实现跨文件共享。
    • 通常在头文件中声明,在源文件中定义。
  • static

    • 全局变量和函数:限制其在定义文件中的可见性,防止外部访问和命名冲突。
    • 局部变量:在函数内部声明,保持变量值在多次函数调用间的持续性。

http://www.ppmy.cn/server/151500.html

相关文章

IOTIQS100芯片, TCP 发送数据+NSOSD,data要是hex16进制转换方法

命令&#xff1a;data以十六进制字符串格式发送的数据。 方法 代码 sprintf(temp, "%02X", data[i]);&#xff1a;将当前字节转换为两位宽的大写十六进制字符&#xff0c;并存储在 temp 中。如果需要小写字母&#xff0c;可以将格式说明符改为 "%02x"。 …

NoSQL大数据存储技术测试(7)键值对数据库Redis和其他NoSQL数据库

单项选择题 第1题 关于Redis的持久化&#xff0c;下列描述错误的是&#xff1a;&#xff08;&#xff09; RDB是以快照的形式&#xff0c;将内存中的数据整体拷贝到硬盘上。 执行RDB存储时会产生阻塞&#xff0c;因此RDB不适合实时备份&#xff0c;而适合定时备份。 AOF是…

【AI日记】24.12.18 kaggle 比赛 2-7

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加&#xff1a;kaggle 比赛 Regression with an Insurance Dataset时间&#xff1a;8 小时 读书 书名&#xff1a;富兰克林自传时间&#xff1a;0.5 小时阅读原因&#xff1a;100 美元纸币上的人物 …

堆排序【东北大学oj数据结构9-4】C++

堆排序是一种基于堆的数据结构的排序&#xff0c;是一种快速排序算法&#xff0c;可以在输入数组中实现排序处理&#xff08;内存高效&#xff09;。 堆排序可以实现如下&#xff1a; maxHeapify(A, i) l left(i) r right(i) // select the node which has the m…

MySQL EXPLAIN 详解:一眼看懂查询计划

在日常的数据库开发中&#xff0c;我们经常需要分析 SQL 查询性能&#xff0c;而 EXPLAIN 是 MySQL 提供的利器&#xff0c;可以帮我们快速理解查询计划&#xff0c;优化慢查询。本文将详细解析 EXPLAIN 的输出字段及其含义&#xff0c;并结合实际案例分享优化思路。 一、什么是…

vue计时器

实现一个倒计时功能用于下单后的计时 代码 倒计时组件 import { ref, onUnmounted } from vue import { computed } from vue import dayjs from dayjsexport const useCountDown () > {// 响应式数据let timer nullconst time ref(0)// 格式化为时分秒const formatTi…

Go 错误处理

Go 错误处理 Go 语言在设计时考虑了错误处理的重要性&#xff0c;提供了一套简洁而强大的错误处理机制。本文将深入探讨 Go 中的错误处理方式&#xff0c;包括错误类型的定义、错误处理的基本模式、以及最佳实践。 错误类型定义 在 Go 中&#xff0c;错误是一个接口&#xf…

C#多线程

C#中的多线程编程是开发高效并发应用程序的关键技术之一&#xff0c;它允许程序同时执行多个任务&#xff0c;从而提升应用程序的响应速度和性能。为了更好地理解C#中的多线程使用和定义&#xff0c;我们可以从以下几个方面来探讨&#xff1a;线程的基本概念、创建线程的方法、…