C++ 头文件说明

如果一个程序足够大,代码功能很多,可以想象,不可能把代码写在一个cpp文件里。我们需要模块化,这样的好处很多,方便分工合作,可读性提高,调用也方便。

这个要怎么做呢?

很简单直接当前cpp文件目录下再新建一个test.cpp,如下:

里面就一句代码:

int a=50;

就行;

然后ConsoleAppliciation1.cpp主程序代码:

#include <iostream>
#include <string>
using namespace std;
extern int a;int main()
{cout << a;
}

然后编译运行(编译器会自动编译所有文件):

很明显,认到了外部变量a; 

关键是这个语句 extern int a;声明a是个外部变量。起了作用,如果没有这个声明,则程序无法正常执行。

这个是针对多文件的吗?

不只是,这个是声明int a 是个外部变量。

只要在main函数里,访问变量a前,按上下顺序没有看到变量的a的定义,都需要用这个extern声明一下,否则编译器不认识。

比如,如下语句:


#include <iostream>
#include <string>
using namespace std;
extern int a;int main()
{cout << b;
}
int b = 60;

可以看到 编译直接报错,如下:

不能识别变量“b",所以我们也要在最前面加上extern int b;即使在同一个文件中。

这样再次编译就正常了。 

那么函数也是同样的道理,比如test.cpp 如下代码:

#include <iostream>
void test()
{std::cout << 100;}

主文件:

#include <iostream>
#include <string>
using namespace std;
void test();
int main()
{test();
}

可以看到在最前面有void test();声明,那么这里为什么没有extern关键字?其实这里你加上也是可以的,因为跟变量不同,函数声明可以省略extern,默认是extern;

那么同理,你的函数定义是main函数后面, 即使在同一个文件中,你也是要声明一下函数的。

那么现在有一个问题,既然访问其它文件的变量需要extern声明一下,如果不加声明,是否可以在两个cpp文件里定义了一个同名的变量。

比如test.cpp

int a;

主文件cpp:

#include <iostream>using namespace std;
int a;
int main() {}

答案是不行,会报错,如下:

如果你很疑惑,那么看下例代码,同样是未声明extern;你就能理解了,跟这个差不多的道理:

#include <iostream>using namespace std;
int a;
int main() {}
int a;

结果都是不能重定义,这一点要明白。

最终的结果是两个cpp,不能定义一样的变量。但这并不是因为全局变量的作用域于所有cpp文件,全局变量作用域只是针对当前cpp文件。

不能定义是因为链接obj时是不能有相同的变量名。所以取消extern声明,也是不能定义相同的变量。

这种现象如果很难理解,你可以在单文件里也找到类似现象。

#include <iostream>using namespace std;
extern int a;
int main() {}
int a;

下面的那个int a的作用域是从定义处开始,到文件结束,不是对于整个CPP文件的,但你不能说,根据作用域,就可以在最上面再定义一个int a了(取消extern声明),这很显然会引起冲突的。 

只不过一个是编译时报错,不能重定义,而两个cpp里不能重定义,是编译通过了(因为编译时都是一次次单独编译CPP文件的),变成中间文件obj 链接的时候会报错,不能有相同的外部链接变量。

那么,可以想象,如果分工合作,在多个cpp文件中,定义全局变量,总会不小心定义了相同的全局变量。

 要如何解决:

1.可以使用static修饰变量,这样的全局变量,就只针对单cpp文件,不能被其它cpp文件访问,也不会引起冲突,如:test.h

static int a;

这样就没问题了。 

2.可以使用命名空间的方法,这里就不示例代码了,只是提供一种思路。

3.那么函数,跟变量是同样的道理,不能在多个cpp文件里重复定义,只能定义一次,然后extern声明引用。如果实在要定义,加上static关键字。声明不被外部链接。只在当前cpp文件里有效。

明白了上面这些,我们可能需要使用大量的声明,变量和函数(以后还会有类)

如果要有多个cpp文件要使用这些,那么每次都打这么一串代码,实在麻烦。而且阅读体验也较差。

所以我们可以把这些声明做成一个头文件。

比如现在有test.cpp文件,如下代码:

#include<iostream>
using namespace std;
int a = 5;
void test()
{cout << "\ntest函数\n";
}

然后是test.h头文件:

 void test();extern int a;

主文件ConsoleApplication1.cpp 包含头文件,然后调用:

#include <iostream>
#include"test.h"
using namespace std;int main() {cout << "a值:" << a;test();
}

运行结果:

这里的#include"test.h",实际是预处理命令,即:将test.h里的代码插入当前位置(非编译).

所以实际是这样:

#include <iostream>
void test();
extern int a;
using namespace std;int main() {cout << "a值:" << a;test();
}

本质跟我们之前手动声明是一样的。

那么这里又有几个问题,既然#include只是简单的插入代码,

或者为什么test.h和test.cpp声明和定义要分开写呢?我全写在test.h里。

首先回答test.h里是可以定义变量,或者具体函数代码,但十分不建议这样写,如果你定义了变量a,前面说过了,多个文件#include test.h,链接时会报重复定义。这样极容易造成混乱。

所以标准的做法是分开写,如果一个cpp里的变量和函数要被其它文件使用,声明部分要分开写成一个头文件。以表示共用。

#ifndef

讲到头文件,这里不得不提一下ifndef 宏定义相关。

比如,将上面test.h代码改成这样:


#ifndef TEST_H
#define TEST_H
void test();
extern int a;#endif

上面的语句意思是,如果没有定义宏TEST_H,则执行#endif之前的语句,就是定义宏TEST_H,然后声明函数test和变量a.

这样做的好处是防止代码重复包含,比如一个cpp里多次引用了头文件,它只会插入一次代码,如果识别到了宏TEST_H. 

如类似这样:

#include <iostream>using namespace std;
#include"test.h"
#include"test.h"
int main() {test();
}

注意这里的宏定义只是对当前cpp文件进行判断,因为本质上宏定义也是预处理,只是单纯替换文本。

所以

#ifndef TEST_H
#define TEST_H

#endif

这些语句,就像你在一个文件里#define max 100

然后你在另一个文件里使用max,是不合法的,只作用于当前文件,所以如果在另一个文件用#ifndef 判断max,肯定也是未定义的。 

引申:那么如果你在test.h里面定义了一个变量a,而不是声明,就不要指望这个#ifndef 帮你解决前面多个文件包含test.h冲突的问题,而且这样即使解决了(比如编译器改了逻辑,宏列表对所有cpp有效),也只是假象,因为它会把代码都清掉了(除了第一次文件),其它文件根本就访问不到这个变量a,自然也不会有冲突了。

我们要做的就是按规矩写代码。这里就告诉了我们为什么要这样做。这样就能避免很多问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/36608.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【蓝桥杯】省赛:分糖果(思维/模拟)

思路 数据很小&#xff0c;直接暴力模拟。 有意思的是一个列表如何当成循环队列写&#xff1f;可以arr[(i1)%n]让他右边超出时自动回到开头。 code import os import sysn int(input()) arr list(map(int,input().split()))ans 0 while 1:arr1 arr.copy()for i in range…

如何理解分布式光纤传感器?

关键词&#xff1a;OFDR、分布式光纤传感、光纤传感器 分布式光纤传感器是近年来备受关注的前沿技术&#xff0c;其核心在于将光纤本身作为传感介质和信号传输介质&#xff0c;通过解析光信号在光纤中的散射效应&#xff0c;实现对温度、应变、振动等物理量的连续、无盲区、高…

【java面型对象进阶】------继承实例

继承结构下的标准Javabean 代码如下&#xff1a; package demo10;//定义员工父类 public class Employee {private String id;private String name;private double salary;//构造方法public Employee(){}public Employee(String id,String name,double salary){this.idid;thi…

matrix-breakout-2-morpheus 靶机----练习攻略 【仅获取shell】

【此练习仅做到反弹shell】 1.靶机下载地址 https://download.vulnhub.com/matrix-breakout/matrix-breakout-2-morpheus.ova 2. 打开靶机&#xff0c;kali使用nmap扫描同C段的主机 找到靶机ip 确保靶机和kali网卡均为NAT模式 先查看kali的ip nmap 192.168.182.1/24 …

解锁MySQL 8.0.41源码调试:Mac 11.6+CLion 2024.3.4实战指南

文章目录 解锁MySQL 8.0.41源码调试&#xff1a;Mac 11.6CLion 2024.3.4实战指南前期准备环境搭建详细步骤安装 CLion安装 CMake 3.30.5准备 MySQL 8.0.41 源码配置 CMake 选项构建 MySQL 项目 调试环境配置与验证配置 LLDB 调试器启动调试验证调试环境 总结与拓展碰到的问题1.…

使用码云搭建CocoaPods远程私有库

一、创建远程私有索引库 用来存放私有框架的详细描述信息.podspec文件 1. 创建私有库 假设码云上创建的私有库为repo-spec 2. 查看本地已存在的索引库 pod repo list 3. 将远程私有索引库添加到本地 pod repo add [https://gitee.com/jingluoguo/repo-spec.git](https://gi…

Devops之AWS:如何安装AWS CLI

AWS 命令行界面&#xff08;AWS CLI&#xff09;是一种开源工具&#xff0c;让我们能够使用命令行 Shell 中的命令与 AWS 服务进行交互。 安装步骤&#xff1a; 下载并运行AWS CLI的MSI安装程序&#xff1a; 点击如下的链接&#xff0c;即可下载MSI安装程序&#xff1a; htt…

docker需要sudo才能使用

一种方法是添加当前用户到docker组里去&#xff0c;当时添加的时候貌似是没问题的&#xff0c;但是现在又不可以了 产生的报错 ❯ docker images Cannot connect to the Docker daemon at unix:///home/ying/.docker/desktop/docker.sock. Is the docker daemon running?解决…

jmeter将返回的数据写入csv文件

举例说明&#xff0c;我需要接口返回体中的exampleid与todoid的数据信息&#xff08;使用边界提取器先将其提取&#xff09;&#xff0c;并将其写入csv文件进行保存 使用后置处理器BeanShell 脚本实例如下 import java.io.*;// 设置要写入的文件路径 String filePath "…

在线教育网站项目第四步:deepseek骗我, WSL2不能创建两个独立的Ubuntu,但我们能实现实例互访及外部访问

一、说明 上一章折腾了半天&#xff0c;搞出不少问题&#xff0c;今天我们在deepseek的帮助下&#xff0c;完成多个独立ubuntu24.04实例的安装&#xff0c;并完成固定ip&#xff0c;实践证明&#xff0c;deepseek不靠谱&#xff0c;浪费我2个小时时间&#xff0c;我们将在下面实…

Apache Paimon 在抖音集团多场景中的优化实践

资料来源&#xff1a;火山引擎-开发者社区 本文将基于抖音集团内部两大业务的典型实时数仓场景&#xff0c;介绍Paimon在抖音集团内部的生产实践。 作者&#xff1a;李明、苏兴、文杰 抖音集团大数据工程师 目前抖音集团内部主要使用 Lambda 架构进行实时数仓建设&#xff0c;其…

PDF Reader Pro for Mac v4.9.0 PDF编辑/批注/OCR/转换工具 支持M、Intel芯片

PDF Reader Pro 是一款用户必备的集管理、编辑、转换、阅读功能于一体的专业的全能PDF阅读专家。快速、易用、强大&#xff0c;让您出色完成 PDF 工作。 应用介绍 PDF Reader Pro&#xff0c;一款功能齐全且强大的PDF阅读和编辑软件。支持PDF阅读、批注、PDF编辑、PDF格式转换…

HTML5与CSS3新特性详解

一、HTML5新特性 1.概述 HTML5 的新增特性主要是针对于以前的不足&#xff0c;增加了一些新的标签、新的表单和新的表单属性等。 这些新特性都有兼容性问题&#xff0c;基本是 IE9 以上版本的浏览器才支持&#xff0c;如果不考虑兼容性问题&#xff0c;可以大量使用这些新特…

shell 脚本搭建apache

#!/bin/bash # Set Apache version to install ## author: yuan# 检查外网连接 echo "检查外网连接..." ping www.baidu.com -c 3 > /dev/null 2>&1 if [ $? -eq 0 ]; thenecho "外网通讯良好&#xff01;" elseecho "网络连接失败&#x…

Linux环境使用jmeter做性能测试

一、安装JDK&#xff0c;版本jdk1.8 1、下载压缩包到/jdk目录下解压 cd /jdk tar -zxvf jdk-8u241-linux-64.tar.gz 2、配置环境变量 在profile文件中末尾新增信息如下所示 vim /etc/profile export JAVA_HOME/usr/local/java/jdk/jdk1.8.0_221 export PATH$PATH:$JAVA_HOM…

巧用符号链接搬移C盘中的软件数据目录到其他盘

#工作记录 我们知道&#xff0c;在Windows11系统&#xff0c;有些软件是不能指定安装目录的&#xff0c;有些软件即使指定了安装目录可是在更新版本之后还是会安装到默认的C盘目录中&#xff08;比如剪映&#xff09;&#xff0c;而且每次安装某些软件之后&#xff0c;这些软件…

Mysql的安装配置

目录 MySQL 简介 MySQL 的下载安装 mysql配置【默认以及下载好】 &#xff08;1&#xff09;配置环境变量 &#xff08;2&#xff09;修改配置文件 &#xff08;3&#xff09;以管理员身份运行cmd&#xff08;一定要用管理员身份运行&#xff0c;不然权限不够&#xff09; …

登山第二十梯:无人机实时自主探索——我是一只小小小鸟

文章目录 一 摘要 二 资源 三 内容 一 摘要 自主探索是无人机 &#xff08;UAV&#xff09; 各种应用的基本问题。最近&#xff0c;基于 LiDAR 的探索因其能够生成大规模环境的高精度点云地图而受到广泛关注。虽然点云本身就为导航提供了信息&#xff0c;但许多现有的勘探方…

基于FPGA轨道交通6U机箱CPCI脉冲板板卡

板卡简介&#xff1a; 本板为脉冲板&#xff0c;脉冲板主要执行CPU下达的指令&#xff0c;通过实现各种控制算法来调节PWM&#xff0c;然后输出光纤PWM信号来驱动变频器功率模块以达到控制电机的目的。 性能规格&#xff1a; 电源&#xff1a;DC5V&#xff1b;15V FPGA&…

一键批量txt转DWG,DWG转txt——插件实现 CAD c#二次开发

如下图&#xff0c;我们有大量dwg需要转为txt格式&#xff0c;或txt格式坐标需要转为dwg格式&#xff0c;此插件可一键完成一个文件夹下所有文件的转换。 插件使用方式 命令行输入&#xff1a; netload 加载此dll插件&#xff0c; 输入&#xff1a; dwg2txt 可将dwg转为t…