当前位置: 首页 > news >正文

C++11: 声明和定义

声明与定义是C/C++中两个核心的概念,也是C/C++区别于其他语言独有的特性。它们对程序的编译和链接过程起着至关重要的作用。

一、C++标准的描述

声明(Declaration):声明告诉编译器某个实体(如变量、函数、类等)存在,并提供足够的类型信息供编译器理解。声明并不涉及该实体内存空间分配或具体实现,只是保证编译阶段能够识别并合法使用该对象。

例如:

extern int x;           // 变量声明
int foo(int, int);      // 函数声明

定义(Definition):定义不仅声明了一个实体的存在,还描述它的具体实现,例如变量的初始化、函数的具体实现等。定义会提供具体的实现。在一个程序中,每个实体只能有且只有一个定义,而声明则可以有多个。

例如:

int x = 5;             // 变量定义,同时也是声明int foo(int a, int b)  // 函数定义
{ return a + b;
}   

二、声明与定义的区别

从编码角度来看,声明与定义的关系可总结为以下几点:

  • 声明与定义的关系:定义本身也是一种声明,因为定义也在告知编译器该实体的存在。然而,声明并不一定是定义。例如,函数原型是一个声明,而函数的实现则是定义。

  • 多个声明,一个定义:对于一个实体对象,C++允许有多个声明,但只能有一个定义。多个模块中的代码可以共享声明,而它们共同依赖的定义则在一个模块中提供。

    例如:

    // 声明可以出现在多个文件中 
    extern int globalVar; // 但定义只能有一次 
    int globalVar = 10;

三、声明与定义的作用

从编译和链接的角度来看,声明和定义发挥着不同的作用:

  • 编译阶段(Compilation):声明主要在编译阶段使用。编译器在编译代码时,只需要知道某个实体的存在及其类型信息,而不需要知道它的具体实现。因此,声明足以使编译器生成相关代码。例如,编译器可以根据声明生成调用某个函数的指令,而无需知道该函数的实现细节。

  • 链接阶段(Linking):定义则在链接阶段起作用。当程序被分成多个模块编译时,最终的链接过程需要将所有模块中的定义结合起来。如果某个符号(变量或函数)有声明但没有定义,链接器将报错,因为它无法找到该符号的具体实现。

四、作用域与可见性

作用域决定了声明的可见性,或者说一个声明在程序中的哪些地方可以被访问。C++中的作用域有以下几种:

  • 局部作用域:在函数或代码块中声明的变量或对象,只在该函数或代码块中可见,函数外部无法访问这些局部声明。

    void func() 
    { int a = 10; // 局部变量 `a` 的作用域仅限于 `func` 函数 
    }
  • 全局作用域:全局变量的声明和定义通常在函数或类的外部进行,这种变量在整个程序中都是可见的。全局变量声明可以多次出现,但定义只能有一次。extern 关键字通常用于在不同文件之间引用全局变量。

    extern int globalVar; // 声明全局变量

五、typedefusing 的区别

在早期的 C++ 版本中,typedef 关键字用于为现有的类型声明一个新的别名。这在代码可读性和简化复杂类型表达方面非常有用。然而,C++11 引入了 using 关键字来取代 typedef,它不仅语法上更简洁,还支持模板的使用。

  • typedef 示例

    typedef unsigned long ulong;
  • using 示例(C++11 引入)

    using ulong = unsigned long;

using 在模板别名中尤其有用,因为 typedef 无法为模板生成别名:

// 使用 typedef 无法为模板定义别名 
template<typename T> typedef std::vector<T> Vec; // 错误 // 使用 using 生成模板别名 
template<typename T> 
using Vec = std::vector<T>;

六、前置声明与循环依赖

前置声明(forward declaration)是一个常见的声明技术,用于解决多个类或函数之间的循环依赖问题。当两个类相互引用时,可以通过前置声明来打破循环依赖,而不必在声明时包含完整的定义。前置声明只是告诉编译器某个类型的名字存在,具体的定义会在后面提供。

例如:

class B; // 前置声明 class A 
{ B* b; // `A` 可以持有指向 `B` 的指针,但此时还不需要知道 `B` 的具体定义 
}; class B 
{ A a;  // `B` 持有 `A` 对象的实例,前置声明解决了循环依赖问题
};

七、C++11声明与定义的新特性

C++11 引入了一些有趣的新特性,进一步增强了声明与定义的灵活性:

  • constexpr 声明:C++11 引入了 constexpr 关键字,用于声明常量表达式函数。这类函数的结果可以在编译时计算,因此既是声明也是定义。

    constexpr int square(int x)
    {return x * x; 
    }
  • 默认与删除的函数声明:C++11 允许为特殊成员函数(如构造函数、赋值运算符等)使用 = default= delete,以显式声明某个函数使用默认实现或被删除。

    class MyClass 
    { 
    public:MyClass() = default; // 使用默认构造函数 MyClass(const MyClass&) = delete; // 禁用拷贝构造函数
    };

八、总结

声明与定义是 C/C++ 语言中不可分割的两个概念。声明告诉编译器某个实体的存在,而定义则提供该实体的具体实现。声明主要用于编译阶段,而定义在链接阶段起作用。作用域决定了声明的可见性,C++11 进一步引入了 usingconstexpr 等新特性,增强了声明与定义的灵活性与可读性。在大型项目中,合理地分离声明与定义、避免循环依赖是高效管理代码的关键。


http://www.mrgr.cn/news/29699.html

相关文章:

  • Docker 命令总结:从入门到入土
  • 艾体宝方案丨制造业BI解决方案:推动智能生产和数字化转型
  • Redis增删改查、复杂查询案例分析
  • 常见git命令记录
  • 网络安全-Linux基础(bash脚本)
  • AI写作(二)NLP:开启自然语言处理的奇妙之旅(2/10)
  • 【C++ Primer Plus习题】16.8
  • 【渗透测试】-vulnhub源码框架漏洞-Os-hackNos-1
  • SQL编程复习(24/9/18)
  • 链表经典面试题
  • LCR 026
  • JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
  • Python 入门教程(4)数据类型 | 4.3、数字类型
  • 请求转发和重定向的区别
  • 掌握Python虚拟环境:隔离项目依赖,提升开发效率的必备指南
  • 【Transformer深入学习】之一:Sinusoidal位置编码的精妙
  • Ubuntu上如何使用sh文件更新CMake
  • Redis - 深入理解Redis事务
  • 微服务配置中心介绍
  • 【学习笔记】IOC容器
  • 《深度学习》—— PyTorch的神经网络模块中常用的损失函数
  • 【AI学习】AI绘画发展简史
  • Qt_多元素控件
  • Fiddler抓包工具实战
  • AutoSar AP中Proxy Class中Methods描述的总结
  • 基于SpringBoot+Vue+MySQL的在线招投标系统