导读:本期聚焦于小伙伴创作的《C++中的ODR(定义一次规则)是什么?如何避免链接时的重复定义错误》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++中的ODR(定义一次规则)是什么?如何避免链接时的重复定义错误》有用,将其分享出去将是对创作者最好的鼓励。

C++中的ODR全称为One Definition Rule,也就是定义一次规则,是C++标准中明确规定的核心规则之一。它的核心要求是,同一个实体(包括变量、函数、类、模板等)在整个程序的所有编译单元中,只能有一个唯一定义,否则在链接阶段就会出现重复定义的错误。

C++中的ODR(定义一次规则)是什么?如何避免链接时的重复定义错误

ODR的具体要求

ODR的适用范围覆盖C++中的大部分实体,不同实体的要求略有区别:

  • 对于非内联的全局函数、全局变量,在整个程序中只能有一个定义,多个编译单元中定义相同名称的全局函数或变量就会触发错误。
  • 对于类类型,同一个类的所有定义必须在词法上完全一致,否则属于未定义行为。
  • 对于模板,同一个模板的定义在不同编译单元中也需要保持完全一致,否则同样不符合ODR要求。

常见的重复定义错误场景

1. 头文件中定义全局变量

很多开发者会在头文件中直接定义全局变量,然后在多个源文件中包含这个头文件,此时每个包含该头文件的编译单元都会有一个该变量的定义,链接时就会报错。

比如下面的头文件config.h

// config.h
int global_count = 0; // 直接定义全局变量,错误做法

如果a.cppb.cpp都包含config.h,编译后链接时就会出现multiple definition of 'global_count'的错误。

2. 头文件中定义非内联函数

非内联的普通函数如果在头文件中定义,被多个源文件包含后也会出现重复定义。比如:

// utils.h
void print_hello() { // 非内联函数直接定义在头文件,错误做法
    printf("hellon");
}

3. 多个源文件中定义同名实体

即使没有包含头文件,如果两个不同的源文件中定义了同名的全局变量或函数,链接时也会触发重复定义错误。

避免重复定义错误的方法

1. 全局变量使用声明+单一定义

头文件中只声明全局变量,在一个源文件中完成定义,其他文件通过extern关键字引用。示例如下:

// config.h 头文件,只做声明
extern int global_count;

// config.cpp 源文件,做唯一的定义
int global_count = 0;

// a.cpp 其他源文件引用
#include "config.h"
void func_a() {
    global_count++;
}

2. 函数定义放在源文件中,头文件只放声明

非内联的函数定义统一放在源文件中,头文件中只保留函数声明,避免多个编译单元出现重复定义。

// utils.h 头文件,只放声明
void print_hello();

// utils.cpp 源文件,放函数定义
#include <stdio.h>
void print_hello() {
    printf("hellon");
}

3. 需要使用内联的场景加inline关键字

如果确实需要在头文件中定义短小的函数,可以添加inline关键字,内联函数允许在多个编译单元中有相同的定义,符合ODR要求。

// utils.h
inline int add(int a, int b) { // 内联函数,允许在头文件定义
    return a + b;
}

4. 类的定义放在头文件,成员函数定义区分场景

类的定义本身可以放在头文件中,因为ODR允许类的多个定义完全一致。类的成员函数如果在类内定义,默认是内联的,没问题;如果在类外定义,非内联的成员函数要放在源文件中,内联的成员函数可以放在头文件中。

// user.h
class User {
public:
    int get_id() const { // 类内定义,默认内联,允许放在头文件
        return id;
    }
    void set_id(int new_id); // 类外非内联声明
private:
    int id;
};

// user.cpp
#include "user.h"
void User::set_id(int new_id) { // 非内联成员函数定义放在源文件
    id = new_id;
}

5. 模板定义放在头文件中

模板的定义通常需要放在头文件中,因为编译器需要在实例化模板时看到完整的定义,只要保证模板在所有编译单元中的定义完全一致,就符合ODR要求。

// array_utils.h
template <typename T>
T get_first(T* arr) { // 模板定义放在头文件,符合要求
    return arr[0];
}

总结

ODR是C++中避免实体重复定义的核心规则,理解它的要求能够帮助开发者规避大部分链接阶段的重复定义错误。实际开发中只要遵循头文件只放声明、全局变量单一定义、需要头文件定义的函数加inline、模板定义放头文件且保持一致这些原则,就能有效避免相关问题,提升代码的规范性和稳定性。

ODR定义一次规则重复定义错误链接错误修改时间:2026-07-08 23:18:16

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。