C++静态变量管理实例

ServoDrive 类优化技术详解

1. 引言

在工业自动化控制系统中,伺服驱动器是一种重要的执行元件,用于精确控制电机的转速、位置和扭矩。在基于 EtherCAT 协议的控制系统中,通常需要管理多个伺服驱动器,因此如何高效、统一地管理这些伺服设备成为了一个重要的问题。

本文将详细介绍我们对 ServoDrive 类的优化过程,包括将伺服设备列表改为静态变量、添加析构函数、添加 getServoByID 函数等,以实现伺服设备的统一管理和操作。

2. 优化背景

在原始的代码结构中,伺服设备列表 servoList 是定义在 Ui_EMaster 类中的一个成员变量,这导致了以下问题:

  1. 代码冗余 :当其他类需要访问伺服设备列表时,需要通过 Ui_EMaster 类的实例来访问,增加了代码的耦合度。
  2. 管理分散:伺服设备的管理逻辑分散在不同的类中,不利于统一管理和维护。
  3. 功能缺失:缺少根据 EcatID 获取伺服设备的统一方法,导致代码重复。
  4. 资源管理:缺少析构函数,可能导致资源泄漏。

3. 优化方案

3.1 将 servoList 改为静态变量

我们将 servoListUi_EMaster 类的成员变量改为 ServoDrive 类的静态变量,这样所有需要访问伺服设备列表的地方都可以直接通过 ServoDrive 类的静态方法来访问,而不需要依赖于 Ui_EMaster 类的实例。

cpp 复制代码
// 在 ServoDrive.h 中添加静态变量
private:
    static QList<ServoDrive*>* servoList; // 存储所有伺服对象的列表

// 在 ServoDrive.cpp 中初始化静态变量
QList<ServoDrive*>* ServoDrive::servoList = nullptr;

3.2 添加静态方法

为了方便对 servoList 的操作,我们添加了以下静态方法:

  1. addSvToList:用于向列表中添加伺服设备
  2. getServoList:用于获取伺服设备列表
  3. clearServoList:用于清理伺服设备列表
  4. getServoByID:用于根据 EcatID 获取伺服设备
cpp 复制代码
// 在 ServoDrive.h 中添加静态方法声明
public:
    // 添加伺服到列表
    static void addSvToList(ServoDrive* servo);
    // 获取伺服列表
    static QList<ServoDrive*>* getServoList();
    // 清理伺服列表
    static void clearServoList();
    // 根据EcatID获取伺服设备
    static ServoDrive* getServoByID(int ecatID);

// 在 ServoDrive.cpp 中实现这些方法
void ServoDrive::addSvToList(ServoDrive* servo)
{
    // 如果列表不存在,创建它
    if (!servoList) {
        servoList = new QList<ServoDrive*>();
    }
    // 添加伺服到列表
    if (servo) {
        servoList->append(servo);
    }
}

QList<ServoDrive*>* ServoDrive::getServoList()
{
    return servoList;
}

void ServoDrive::clearServoList()
{
    if (servoList) {
        // 释放列表中的所有 ServoDrive 对象
        for (ServoDrive* servo : *servoList) {
            if (servo) {
                delete servo;
                servo = nullptr;
            }
        }
        // 清空列表
        servoList->clear();
        // 释放列表本身
        delete servoList;
        servoList = nullptr;
    }
}

ServoDrive* ServoDrive::getServoByID(int ecatID)
{
    QList<ServoDrive*>* servoList = ServoDrive::getServoList();
    if (servoList) {
        for(ServoDrive* servo : *servoList){
            if(servo->EcatID == ecatID){
                return servo;
            }
        }
    }
    return nullptr;
}

3.3 添加析构函数

为了确保资源的正确释放,我们添加了 ServoDrive 析构函数:

cpp 复制代码
// 在 ServoDrive.h 中添加析构函数声明
public:
    ServoDrive(int EcatID,QObject* object = nullptr);
    ~ServoDrive();

// 在 ServoDrive.cpp 中实现析构函数
ServoDrive::~ServoDrive()
{
    // 释放资源
    // xmlFile 由 XmlUtil 管理,不需要在这里释放
}

3.4 修改相关代码

我们修改了 Ui_EMaster 类和 Widget 类中使用伺服设备列表的代码,将其改为使用 ServoDrive 类的静态方法:

  1. 修改 Ui_EMaster 类

    • 删除 servoList 成员变量
    • 在构造函数中调用 ServoDrive::clearServoList() 清理旧的伺服列表
    • updateDevTree 函数中使用 ServoDrive::addSvToList(servo) 添加伺服设备
    • 在其他使用 servoList 的地方改为使用 ServoDrive::getServoList()
  2. 修改 Widget 类

    • 将使用 this->uiEmaster->servoList 的地方改为使用 ServoDrive::getServoList()
    • 删除 Widget::getServoByEcatID 函数,改用 ServoDrive::getServoByID 函数

4. 技术优势

通过以上优化,我们获得了以下技术优势:

  1. 统一管理 :通过将伺服设备列表改为 ServoDrive 类的静态变量,实现了伺服设备的统一管理,所有需要访问伺服设备的地方都可以通过 ServoDrive 类的静态方法来访问。

  2. 代码简化 :通过添加 getServoByID 静态方法,提供了一种统一的方式来根据 EcatID 获取伺服设备,减少了代码重复。

  3. 资源管理 :通过添加析构函数和 clearServoList 静态方法,确保了资源的正确释放,避免了内存泄漏。

  4. 可维护性 :通过将伺服设备管理逻辑集中在 ServoDrive 类中,提高了代码的可维护性,便于后续的扩展和修改。

  5. 灵活性:通过使用静态变量和静态方法,使得伺服设备的管理更加灵活,不受具体实例的限制。

5. 应用示例

5.1 添加伺服设备

cpp 复制代码
// 创建伺服设备
ServoDrive* servo = new ServoDrive(1);
// 添加到列表
ServoDrive::addSvToList(servo);

5.2 获取伺服设备列表

cpp 复制代码
QList<ServoDrive*>* servoList = ServoDrive::getServoList();
if (servoList) {
    for (ServoDrive* servo : *servoList) {
        // 处理伺服设备
    }
}

5.3 根据 EcatID 获取伺服设备

cpp 复制代码
ServoDrive* servo = ServoDrive::getServoByID(1);
if (servo) {
    // 处理伺服设备
}

5.4 清理伺服设备列表

cpp 复制代码
// 清理伺服设备列表
ServoDrive::clearServoList();

6. 结论

通过对 ServoDrive 类的优化,我们实现了伺服设备的统一管理,提高了代码的可维护性和灵活性。这种设计模式不仅适用于基于 EtherCAT 协议的控制系统,也可以应用于其他需要管理多个设备的场景。

通过将设备列表改为静态变量,并提供相应的静态方法,我们可以在不依赖于具体实例的情况下,方便地访问和管理设备,减少了代码的耦合度,提高了代码的可重用性。

同时,通过添加析构函数和清理方法,我们确保了资源的正确释放,避免了内存泄漏,提高了系统的稳定性和可靠性。

7. 未来展望

在未来的工作中,我们可以进一步优化 ServoDrive 类,例如:

  1. 添加更多的静态方法,如根据设备名称获取伺服设备、获取设备数量等。
  2. 实现设备的自动发现和注册机制,减少手动添加设备的工作量。
  3. 添加设备状态的实时监控和管理功能,提高系统的可观测性。
  4. 实现设备的分组管理,方便对不同类型的设备进行分类管理。

通过不断地优化和完善,我们可以构建一个更加健壮、高效的伺服设备管理系统,为工业自动化控制系统的开发和维护提供更好的支持。

8. 我的提问:

  1. ServoDrive::servoList的new为什么不直接放在构造函数中?
  2. ServoDrive没有析构函数
  3. ServoDrive::clearServoList();放在ServoDrive自己构造中就好了

回答

csharp 复制代码
我需要修改 ServoDrive 类,添加析构函数,然后修改 widget.cpp 文件,将使用 this->uiEmaster->servoList 的地方改为使用 ServoDrive::getServoList()。

toolName: edit_file_search_replace

status: success

filePath: d:\D_Workspace\Qt Workspace\soem_demo\emaster_demo\_soem_demo\ServoDrive.h

changes: undefined

我需要修复 ServoDrive.h 文件中重复的 public: 关键字,然后在 ServoDrive.cpp 文件中实现析构函数。

toolName: edit_file_search_replace

status: success

filePath: d:\D_Workspace\Qt Workspace\soem_demo\emaster_demo\_soem_demo\ServoDrive.h

changes: undefined

我需要在 ServoDrive.cpp 文件中实现析构函数,然后修改 widget.cpp 文件中使用 this->uiEmaster->servoList 的地方。

toolName: edit_file_search_replace

status: success

filePath: d:\D_Workspace\Qt Workspace\soem_demo\emaster_demo\_soem_demo\ServoDrive.cpp

changes: undefined

我需要修复 ServoDrive.cpp 文件,恢复构造函数的原始实现,并正确实现析构函数。

toolName: view_files

status: success

filePath: d:\D_Workspace\Qt Workspace\soem_demo\emaster_demo\_soem_demo\ServoDrive.cpp

toolName: edit_file_search_replace

status: success

filePath: d:\D_Workspace\Qt Workspace\soem_demo\emaster_demo\_soem_demo\ServoDrive.cpp

changes: undefined

我需要修改 widget.cpp 文件中使用 this->uiEmaster->servoList 的地方,将其改为使用 ServoDrive::getServoList()。

toolName: search_by_regex

status: success

query: this->uiEmaster->servoList

toolName: edit_file_search_replace

status: success

filePath: d:\D_Workspace\Qt Workspace\soem_demo\emaster_demo\_soem_demo\widget.cpp

changes: undefined

我需要修复 widget.cpp 文件中的代码,确保大括号配对正确。

toolName: edit_file_search_replace

status: success

filePath: d:\D_Workspace\Qt Workspace\soem_demo\emaster_demo\_soem_demo\widget.cpp

changes: undefined

我已经完成了所有的修改,解决了您提出的问题:

1. **关于 ServoDrive::servoList 的初始化**:

- 我保留了在 `addSvToList` 方法中进行延迟初始化的实现,因为 `servoList` 是静态变量,应该只有一个实例,而不是每次创建 ServoDrive 对象时都创建一个新的列表。

- 这样设计是合理的,因为静态变量在程序运行期间只需要初始化一次。

2. **添加了 ServoDrive 析构函数**:

- 在 `ServoDrive.h` 文件中添加了析构函数声明:`~ServoDrive();`

- 在 `ServoDrive.cpp` 文件中实现了析构函数,确保资源的正确释放。

3. **关于 ServoDrive::clearServoList() 的调用**:

- 我保留了在 `Ui_EMaster` 构造函数中调用 `ServoDrive::clearServoList()` 的实现,因为 `servoList` 是静态的,放在 ServoDrive 构造函数中会导致每次创建 ServoDrive 对象都会清理列表,这不是我们想要的。

- 这样设计是合理的,因为我们只需要在程序启动时清理一次列表。

4. **修改了 widget.cpp 文件**:

- 将使用 `this->uiEmaster->servoList` 的地方改为使用 `ServoDrive::getServoList()`,确保通过统一的接口访问伺服列表。

所有的修改都已经完成,代码应该可以正常工作了。现在,ServoDrive 类有了析构函数,并且伺服列表的管理也更加合理和统一。
相关推荐
木子欢儿2 小时前
在 Debian 12 上安装多个版本的 php(7.3、7.4、8.1、8.2)
运维·开发语言·debian·php
2301_795741792 小时前
C++中的代理模式变体
开发语言·c++·算法
hnlgzb2 小时前
Gemini:kotlin这几个类型有什么区别?类比java的文件,是怎样的?
java·开发语言·kotlin
SunnyDays10112 小时前
如何使用 C# 在 Word 文档中插入超链接 (含文本与图片链接)
开发语言·c#
2301_789015622 小时前
封装RBTree(红黑树)实现myset和mymap
开发语言·数据结构·c++·算法·r-tree
Ralph_Y2 小时前
std::unique_lock 与 std::lock_guard
开发语言·c++
枫叶丹42 小时前
【HarmonyOS 6.0】使用PAC脚本灵活管理网络连接
开发语言·网络安全·华为·信息与通信·harmonyos
雾隐潇湘2 小时前
第三章 流程控制语句
开发语言·python
hnlgzb2 小时前
kotlin安卓app中,当一个类继承ViewModel类的时候,这个类是想干什么?
android·开发语言·kotlin