C++学习笔记(四): 类、头文件、对象

一个类定义了一个类型,以及与其关联的一组操作。所谓类,是用户自定义的数据类型。

类机制是C++最重要的特性之一。实际上,C++最初的一个设计焦点就是能定义使用上像内置类型一样自然的类类型(class type)。

在这里插入图片描述
类的定义一般分为两部分,其中之一是“头文件”,用来声明类所提供的各种操作行为,另一文件是“程序源代码文件”,包含这些操作行为的实现内容。

//头文件Box.h
#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
      // 成员函数声明
      double get(void);
      void set( double len, double bre, double hei );
};

为了使用标准库设施,我们必须包含相关的头文件。类似的,我们也需要使用头文件来访问为自己的应用程序所定义的类。习惯上,头文件根据其中定义的类的名字来命名。

// 程序源代码文件Box.cpp
// 成员函数定义
double Box::get(void)
{
    return length * breadth * height;
}
 
void Box::set( double len, double bre, double hei)
{
    length = len;
    breadth = bre;
    height = hei;
}
//主入口文件main.cpp
#include <iostream>
#include "Box.h"

int main( )
{
   Box Box1;        // 声明 Box1,类型为 Box
   double volume = 0.0;     // 用于存储体积
   
   // box 1 详述
   Box1.height = 5.0; 
   Box1.length = 6.0; 
   Box1.breadth = 7.0;
   
   // box 1 的体积
   Box1.set(16.0, 8.0, 12.0); 
   volume = Box1.get(); 
   return 0;
}

1.认识头文件

此程序以两个#include指令开始,其中一个使用了新的形式。
包含来自标准库的头文件时,也应该用尖括号(<>)包围头文件名。
对于不属于标准库的头文件,则用双引号(" ")包围。

1.1 C++为什么会有头文件?

为什么不扫描所有.c文件,然后把其中的函数定义都找出来,生成统一的一个声明文件,以后编译每个.c文件的时候都依赖这个文件就可以了。类似于java那样。
原因如下:
(1)如果编译器想成功编译并链接某个源码文件,需要知道两点信息:数据类型和重定位信息。以Java为例,Java二进制包内存放有数据类型信息和重定位信息;而C++二进制库符号表内只存放了重定位信息,因此,若想成功编译链接C++源码,还需要头文件告知编译器数据类型信息。而早期的硬件存储空间还是运算能力都还不太够,通过.h/.c手动的把声明和实现分开来解决问题是个非常简单有效的办法。
(2)C语言设计之初是没有头文件的,数据都是定长的。但是后来出现了 short int 、long int ;char等类型,函数的调用者不知道压栈的长度。例如函数add(x,y)调用:应该是先压2个字节、再压4个字节喃,还是先压4个字节,再压2个字节;还是连续压2个4字节?在这种情况下,函数调用需要提前声明,以便让调用者得知函数的参数与返回值尺寸。
(3)C++最初是作为C语言的扩展而开发的,因此它继承了C语言的一些特性,例如单独编译和模块化设计。C++有头文件是要兼容C,而 C++ 的Module包含元数据信息还在委员会讨论之中。
(4)使用头文件的好处是可以将代码分为接口和实现两部分,接口定义在头文件中,实现定义在源文件中。这样可以避免每次都重新编译整个程序,提高编译速度。还可以有效地隐藏实现细节,提高代码的可读性和可维护性。
(5)此外,使用头文件还可以方便地共享代码,因为多个源文件可以包含同一头文件,从而共享其中的代码。

头文件(header)使类或其他名字的定义可被多个程序使用的一种机制。程序通过#include指令使用头文件。

1.2 如何编写头文件?

头文件通常包含那些只能被定义一次的实体,如类、const和constexpr变量等。头文件也经常用到其他头文件的功能。

编写头文件头文件常用的技术是预处理器

在C++中,预处理器是一种在编译之前对源代码进行预处理的程序。它主要用于实现一些在编译时无法完成的任务,例如包含头文件、宏定义、条件编译等。

(1)包含头文件:使用#include指令可以包含头文件,以便访问其中定义的函数、变量、类等内容。

#include <iostream>
#include "myheader.h"

(2)宏定义:使用#define指令可以定义一个宏,以便在源代码中使用。宏可以定义一个常量、一个函数、一个代码块等。

#define PI 3.1415926
#define MAX(a, b) ((a) > (b) ? (a) : (b))

(3)条件编译:使用#if、#ifdef和#ifndef等指令可以进行条件编译,以便根据条件选择编译不同的代码。
例如,我们使用#ifdef和#endif指令进行条件编译,当定义了DEBUG宏时,输出Debug mode;否则,输出Release mode。

#ifdef DEBUG
    cout << "Debug mode" << endl;
#else
    cout << "Release mode" << endl;
#endif

(4)文件包含:使用#include指令可以包含其他源代码文件,以便将多个源代码文件组合成一个程序。例如,我们使用#include指令包含了myheader.h和mysource.cpp两个源代码文件,以便将多个源代码文件组合成一个程序。

#include "myheader.h"
#include "mysource.cpp"

预处理器的主要作用是在编译之前对源代码进行处理,以便生成一个新的源代码文件,这个文件包含了预处理器指令所表示的内容。预处理器可以使代码更加模块化、可读、易于维护,从而提高编程效率和代码质量。

编写头文件时应该遵循以下规范:

(1)头文件应该包含防止重复包含的预处理器指令。例如,使用#ifndef、#define和#endif指令可以防止头文件被多次包含。

正确用法:

#ifndef MYCLASS_H
#define MYCLASS_H

#include <iostream>

// MyClass类的声明
class MyClass {
public:
    MyClass(int count);
    void print();
private:
    int m_count;
};

// MyClass类的实现
MyClass::MyClass(int count) : m_count(count) {}

void MyClass::print() {
    std::cout << "count = " << m_count << std::endl;
}

#endif

错误示例:

//包含了同一个头文件myheader.h两次
//并没有使用#ifndef、#define和#endif等预处理器指令来防止重复包含,从而导致链接错误。
#include "myheader.h"
#include "myheader.h"

(2)头文件应该包含必要的声明和定义,但不应该包含不必要的内容。例如,不应该在头文件中包含函数的实现代码,因为这会导致多个源代码文件包含同一个实现代码,从而导致链接错误。

错误示例1:

//多次包含同一个头文件
#include <iostream>
#include <iostream>

错误示例2:

//包含了string头文件,但是并没有在程序中使用string类型或相关函数。
//这样会增加编译时间和可执行文件大小,降低程序的性能。
#include <iostream>
#include <string>

(3)头文件应该使用全局唯一的命名规范,避免与其他头文件或变量名称冲突。一般建议使用项目名或类名作为头文件的前缀,例如MyClass.h。

(4)头文件应该避免包含其他头文件,除非这些头文件是必需的。如果必须包含其他头文件,应该在头文件中使用前向声明来避免循环包含。

错误示例:

//包含了myheader.h头文件,但在函数中使用了MyClass类型,却没有使用前向声明来告知编译器这个类型的存在。
//这会导致编译器无法在编译函数前找到MyClass类型的定义,从而导致编译错误。
#include "myheader.h"

// 使用MyClass类型
void func(MyClass obj) {
    // ...
}

正确用法:

// 前向声明MyClass类
class MyClass;

// 使用MyClass类型
void func(MyClass obj) {
    // ...
}

// 包含MyClass类的头文件
#include "myclass.h"
//在上面的代码中,我们使用class MyClass;语句进行了MyClass类的前向声明
//然后在函数中使用了MyClass类型。
//最后,我们包含了MyClass类的头文件myclass.h,以便访问MyClass类的实现。

关于前向声明的解释:

// Forward declaration of class B
class B;

// Class A uses class B
class A {
public:
    void doSomething(B& b);
};

// Class C uses class A and class B
class C {
public:
    void doSomethingElse(A& a, B& b);
};

// Class B definition
class B {
public:
    void doSomething();
};

// Implementation of A::doSomething()
void A::doSomething(B& b) {
    b.doSomething();
}

// Implementation of C::doSomethingElse()
void C::doSomethingElse(A& a, B& b) {
    a.doSomething(b);
    b.doSomething();
}

在这个例子中,类C同时使用了类A和类B,如果直接在类C的头文件中包含类A和类B的头文件,很容易造成循环包含。使用前向声明可以避免这个问题。同时,由于类A和类B的定义分别在其他头文件中,所以在类A和类B的头文件中也必须使用前向声明来避免循环包含。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java之旅(三)

Java 输出&#xff1a;println()、print() 使用 println() 方法在 Java 中输出值或打印文本。 System.out.println("Hello World!"); println() 可以根据需要添加任意数量的方法。请注意&#xff0c;它将为每个方法添加一个新行&#xff1a; System.out.println(&…

Unity2D骨骼动画制作之单张图片编辑

1、打开骨骼制作面板 在Sprite Editor左侧选项&#xff0c;选择Skinning Editor 2、 &#xff08;1&#xff09;骨骼制作 Preview Pose 预览模式&#xff0c;可以预览动作并不会真正的改变设置 Reset Pose 将角色骨骼和关节恢复到原始位置 Edit Bone 编辑骨骼&#xff0c;…

SUSE系统上安装HANA

一:安装SUSE操作系统 1.1 准备安装镜像 SLE-15-SP1-安装程序-DVD-x86_64-GM-DVD1 SLE-15-SP1-软件包-x86_64-GM-DVD1 SAP HANA安装文件 IMDB_SERVER20_032_0-80002031.SAR 1.2 引导系统 1.3 选择要安装的产品 SUSE Linux Enterprise Server for SAP Applications 15 SP…

【MySQL】MySQL间隙锁--幻读解决原理

文章目录 一、间隙锁概念二、测试间隙锁范围加锁三、测试等值间隙锁 一、间隙锁概念 当我们用范围条件而不是相等条件检索数据&#xff0c; 并请求共享或排他锁时&#xff0c;InnoDB 会给符合条件的已有数据记录的索引项加锁&#xff1b;对于键值在条件范围内但并不存在的记录…

chatgpt赋能python:用Python做股票分析

用Python做股票分析 在当今的股市中&#xff0c;数据分析和预测已经变得十分重要。Python作为最流行的编程语言之一&#xff0c;不仅易于学习&#xff0c;还有非常强大的数据处理和分析能力。在本文中&#xff0c;我们将探讨如何用Python进行股票分析。 数据收集 要进行股票…

如何利用宝塔面板快速搭建Wordpress网站?

本章教程&#xff0c;主要介绍一下&#xff0c;如何利用宝塔面板快速搭建Wordpress网站。 目录 一、 前置条件 二、 打开宝塔面板 三、解析域名 四、安装界面 五、主题安装 六、网站预览 一、 前置条件 需要准备一台Linux服务器&#xff0c;系统版本使用centos 7.X。 使用…

【项目】ROS下使用乐视深度相机LeTMC-520

本文主要记录如何在ros下使用乐视深度相机。乐视三合一体感摄像头LeTMC-520其实就是奥比中光摄像头&#xff08;Orbbec Astra Pro&#xff09; 系统&#xff1a;Ubuntu20.04 这款相机使用uvc输入彩色信息&#xff0c;需要使用libuvc、libuvc_ros才能在ROS上正常使用彩色功能。…

Vue.js 中的父子组件通信方式

Vue.js 中的父子组件通信方式 在 Vue.js 中&#xff0c;组件是构建应用程序的基本单元。当我们在应用程序中使用组件时&#xff0c;组件之间的通信是非常重要的。在 Vue.js 中&#xff0c;父子组件通信是最常见的组件通信方式之一。在本文中&#xff0c;我们将讨论 Vue.js 中的…

spring boot使用elasticsearch分词,排序,分页,高亮简单示例

目录 1. 创建ES实体2. 创建查询实体3. 查询方法实现3.1 核心代码3.2 构建查询条件3.2.1 关键词分词 3.3 高亮处理 4.完整查询代码展示 记&#xff0c;写一个简单的es分词demo,es版本6.8.12 如果使用es7有些方法可能会有所改变&#xff0c;请参考7的文档 es安装教程&#xff1a;…

隔离驱动芯片SLMi332兼容光耦隔离驱动器时的注意事项

数明深力科SLMi33x系列SLMi332是一款兼容光耦带DESAT保护功能的IGBT/SiC隔离驱动器。内置快速去饱和(DESAT) 故障检测功能、米勒钳位功能、漏极开路故障反馈、软关断功能以及可选择的自恢复模式&#xff0c;兼容光耦隔离驱动器。 SLMi33x系列SLMi332的DESAT阈值为6.5V&#xf…

大数据---聚类分析概述及聚类评估

聚类概述: 什么是聚类&#xff1f; 是把数据对象集合按照相似性划分成多个子集的过程。每个子集是一个簇&#xff08;cluster&#xff09;&#xff0c;分类的最终效果&#xff1a;使得簇中的对象彼此相似&#xff0c;但与其他簇中的对象相异。聚类是无监督学习&#xff0c;因…

缓存被穿透了怎么办?

首先来了解几个概念&#xff1a; 缓存穿透&#xff1a;大量请求根本不存在的key 缓存雪崩&#xff1a;redis中大量key集体过期 缓存击穿&#xff1a;redis中一个热点key过期&#xff08;大量用户访问该热点key&#xff0c;但是热点key过期&#xff09; 穿透解决方案 对空值…

如何有效和快速清理C盘

电脑在运行过程中会产生磁盘碎片&#xff0c;时间一长垃圾文件就会越多。而且我们平常不敢乱清理C盘中的文件&#xff0c;以免因为误删导致系统出现故障&#xff0c;所以垃圾文件才肆意占用系统盘空间。不过我们可以选择系统自带的“磁盘清理”功能“制服”它&#xff0c;给C盘…

带电更换柱上变压器(综合不停电作业法)

一、现场复勘 1.核对工作线路双重名称、杆号及设备双重名称 2.检查杆身质量 3.检查线路装置是否符合带电作业要求 4.检查待更换变压器容量 满足旁路作业要求 5.检查气象条件 作业前进行湿度和风速的测量&#xff0c;风力大于5级或湿度大于80%时&#xff0c;不宜带电作业&…

华为开源自研AI框架昇思MindSpore应用案例:Pix2Pix实现图像转换

目录 一、环境准备1.进入ModelArts官网2.使用CodeLab体验Notebook实例 二、基础原理三、准备环节配置环境文件准备数据数据展示 四、创建网络生成器G结构定义UNet Skip Connection Block基于UNet的生成器 基于PatchGAN的判别器Pix2Pix的生成器和判别器初始化 五、训练六、推理 …

SWAT模型教程

详情点击链接&#xff1a;SWAT模型教程详情点击链接&#xff1a;SWAT模型&#xff08;建模方法、实例应用、高级进阶&#xff09; 一&#xff1a;基于网络资源的SWAT模型快速建模​ 二&#xff1a;基于遥感产品的SWAT模型率定与验证​ 三&#xff1a;基于水文响应单元&#xff…

每日一题——用两个队列实现栈

每日一题 用两个队列实现栈 题目链接 思路 这里主要讲怎么实现出栈StackPop操作做完用两个栈实现队列&#xff0c;我们可能会想当然的认为&#xff0c;这一题也是一个主队列&#xff0c;一个辅助队列&#xff0c;当要出队时&#xff0c;首先判断辅助队列是否为空&#xff0c;…

FineBI6.0基础学习第一课 数据门户

PC端门户使用示例 首先,以管理员身份登录FineBI系统,安装数据门户,安装步骤见官网 新建一个数据门户

C++中的高阶函数:以std::function优雅地实现回调

C中的高阶函数&#xff1a;以std::function优雅地实现回调 1. 简介1.1 C高阶函数的概念1.2 C的std::function的功能及其重要性 2. std::function的使用2.1 std::function的定义和基本使用2.1.1 std::function的定义2.1.2 std::function的基本使用 2.2 std::function接受普通函数…

Python+Yolov5人脸表情特征识别

程序示例精选 PythonYolov5人脸表情特征识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonYolov5人脸表情特征识别>>编写代码&#xff0c;代码整洁&#xff0c;规则&am…