C++基础教程面向对象(学习笔记(9))

本文介绍了在C++中如何在类定义之外定义成员函数,解释了这种做法如何帮助管理和使用复杂的类。讨论了类定义和成员函数定义分别放置在头文件和源文件中的好处,以及默认参数和单定义规则的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类代码和头文件
在类定义之外定义成员函数

到目前为止我们编写的所有类都非常简单,我们已经能够直接在类定义本身内实现成员函数。例如,这是我们无处不在的Date类:

class Date
{
private:
    int m_year;
    int m_month;
    int m_day;
 
public:
    Date(int year, int month, int day)
    {
        setDate(year, month, day);
    }
 
    void setDate(int year, int month, int day)
    {
        m_year = year;
        m_month = month;
        m_day = day;
    }
 
    int getYear() { return m_year; }
    int getMonth() { return m_month; }
    int getDay()  { return m_day; }
};

但是,随着类变得越来越复杂,在类中包含所有成员函数定义会使类更难管理和使用。使用已经编写的类只需要了解它的公共接口(公共成员函数),而不需要了解类的工作原理。成员函数实现细节带来的只有妨碍。

幸运的是,C ++提供了一种将类的“声明”部分与“实现”部分分开的方法。这是通过在类定义之外定义类成员函数来完成的。为此,只需将类的成员函数定义为正常函数,但使用作用域解析运算符(::)将类名称作为函数前缀(与命名空间相同)。

这是我们的Date类,其中Date构造函数和setDate()函数在类定义之外定义。请注意,这些函数的原型仍存在于类定义中,但实际实现已移到外部:

class Date
{
private:
    int m_year;
    int m_month;
    int m_day;
 
public:
    Date(int year, int month, int day);
 
    void SetDate(int year, int month, int day);
 
    int getYear() { return m_year; }
    int getMonth() { return m_month; }
    int getDay()  { return m_day; }
};
 
// Date构造函数
Date::Date(int year, int month, int day)
{
    SetDate(year, month, day);
}
 
// Date 成员函数
void Date::SetDate(int year, int month, int day)
{
    m_month = month;
    m_day = day;
    m_year = year;
}

这非常简单。因为访问函数通常只有一行,所以它们通常留在类定义中,即使它们可以移到外面。

这是另一个包含具有成员初始化列表的外部定义构造函数的示例:

class Calc
{
private:
    int m_value = 0;
 
public:
    Calc(int value=0): m_value(value) {}
 
    Calc& add(int value) { m_value  += value;  return *this; }
    Calc& sub(int value) { m_value -= value;  return *this; }
    Calc& mult(int value) { m_value *= value;  return *this; }
 
    int getValue() { return m_value ; }
};

变为:

class Calc
{
private:
    int m_value = 0;
 
public:
    Calc(int value=0);
 
    Calc& add(int value);
    Calc& sub(int value);
    Calc& mult(int value);
 
    int getValue() { return m_value; }
};
 
Calc::Calc(int value): m_value(value)
{
}
 
Calc& Calc::add(int value)
{
    m_value += value;
    return *this;
}
 
Calc& Calc::sub(int value) 
{
    m_value -= value;
    return *this;
}
 
Calc& Calc::mult(int value)
{
    m_value *= value;
    return *this;
}

将类定义放在头文件中

在头文件的课程中,您了解到可以将函数声明放在头文件中,以便在多个文件甚至多个项目中使用这些函数。类也不例外。类定义可以放在头文件中,以便于在多个文件或多个项目中重用。传统上,类定义放在与类同名的头文件中,并且在类外定义的成员函数放在与该类同名的.cpp文件中。

这是我们的Date类,分为.cpp和.h文件:

Date.h:


#ifndef DATE_H
#define DATE_H
 
class Date
{
private:
    int m_year;
    int m_month;
    int m_day;
 
public:
    Date(int year, int month, int day);
 
    void SetDate(int year, int month, int day);
 
    int getYear() { return m_year; }
    int getMonth() { return m_month; }
    int getDay()  { return m_day; }
};
 
#endif

Date.cpp:

#include "Date.h"
 
// Date构造函数
Date::Date(int year, int month, int day)
{
    SetDate(year, month, day);
}
 
// Date成员函数
void Date::SetDate(int year, int month, int day)
{
    m_month = month;
    m_day = day;
    m_year = year;
}

现在任何其他想要使用Date类的头文件或代码文件都可以#include “Date.h”。请注意,Date.cpp也需要编译到使用Date.h的任何项目中,以便链接器知道Date的实现方式。

不在头文件中定义类违反了单定义规则吗?

它不至于。如果您的头文件具有正确的标头保护关于标头保护请看这个,则不应该将类定义多次包含在同一文件中。

类型(包括类)不受单定义规则的约束,即每个程序只能有一个定义。因此,没有问题#include包括多个代码文件的类定义(如果有的话,类不会有太多用处)。

不在头文件中定义成员函数违反了单定义规则吗?

这取决于在类定义中定义的成员函数被认为是隐式内联的。内联函数不受单定义规则的每个程序部分的一个定义的限制。这意味着在类定义本身内定义普通成员函数(例如访问函数)没有问题。

在类定义之外定义的成员函数被视为普通函数,并且遵循单定义规则的每个程序部分的一个定义。因此,这些函数应该在代码文件中定义,而不是在头文件中定义。这方面的一个例外是模板功能,我们将在以后的章节中介绍。

那么应该在头文件和.cpp文件中定义什么,以及类定义与外部的内容是什么?

您可能想要将所有成员函数定义放入类中的头文件中。虽然这会编译,但这样做有几个缺点。首先,如上所述,这会使您的类定义变得混乱。其次,类内定义的函数是隐式内联的。对于从许多地方调用的较大函数,这可能会使代码膨胀。第三,如果你更改了头文件中代码的任何内容,那么你需要重新编译包含该标题的每个文件。这可能会产生连锁反应,其中一个小的更改会导致整个程序需要重新编译(这可能很慢)。如果更改.cpp文件中的代码,则只需重新编译该.cpp文件!

因此,建议如下:

a:对于通常不可重用的一个文件中使用的类,直接在它们使用的单个.cpp文件中定义它们。
b:对于在多个文件中使用的类或用于一般重用的类,请在与该类同名的.h文件中定义它们。
c:可以在类中定义普通成员函数(普通构造函数或析构函数,访问函数等)。
d:应该在与该类同名的.cpp文件中定义不重要的成员函数。
在将来的课程中,我们的大多数类将在.cpp文件中定义,所有函数都直接在类定义中实现。这只是为了方便并保持示例简短。在实际项目中,将类放入自己的代码和头文件中更为常见,应该习惯这样做。

默认参数

成员函数的默认参数应该在类定义中(在头文件中)声明,#includes头部可以看到它们。

Libraries

对于可用于扩展程序的库,分离类定义和类实现非常常见。在整个程序中,您拥有属于标准​​库的#included标头,例如iostream,string,vector,array等。请注意,您不需要将iostream.cpp,string.cpp,vector.cpp或array.cpp添加到项目中。您的程序需要头文件中的声明,以便编译器验证您正在编写语法正确的程序。但是,属于C ++标准库的类的实现包含在链接阶段链接的预编译文件中。你永远不会看到代码。

在一些开源软件(提供.h和.cpp文件)之外,大多数第三方库只提供头文件以及预编译的库文件。这有几个原因:1)链接预编译库比每次需要时重新编译它更快
2)许多应用程序可以共享预编译库的单个副本,而编译后的代码可以编译到每个可执行文件中它使用它(膨胀文件大小)
3)知识产权的原因(你不希望人们窃取你的代码)。

将您自己的文件分成声明(头文件)和实现(代码文件)不仅是一种好的形式,它还使您可以更轻松地创建自己的自定义库。创建自己的库超出了我学的教程的范围,但将声明和实现分开是执行此操作的先决条件。

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人运动设备中的六轴姿态传感器,它集成了三轴陀螺仪三轴加速度计。这款传感器能够实时监测并提供设备的角速度线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp``MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口函数声明。它定义`MPU6050`,该包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式I2C地址,`getAcceleration()``getGyroscope()`则分别用于获取加速度角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()``getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准滤波,以消除噪声漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到似的库来驱动MPU6050。STM32通常具有更强大的处理能力更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客其他开源资源是学习解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值