在 C++ 中创建自定义库时如何定义多个函数使用的变量

问题描述 投票:0回答:1

我想要一些关于如何摆脱重复变量的指导。

    char Temp_File[40] ="/";
    char End_File[8] = ".csv";
    File Parameter_File;

我不确定在哪里声明这些变量,以便它们可以被库中的所有函数使用,但不会干扰主程序使用库时(此处不包括)。

这是一个关于 esp32-C3 芯片的项目,我正在使用 Arduino IDE 对所述芯片进行编程

这是我的库的 .h 和 .cpp 文件

Info_Saver.h 文件

#ifndef INFO_SAVER_H
#define INFO_SAVER_H

#include "FS.h"
#include "FFat.h"

namespace Info_Saver{
    class Info_Saver {
        #define FORMAT_FFAT true
        public:
            void Save_Info(char * File_Name, int Value_In);
  
            void Format_Space();
    
            void Delete_Parameter(char * File_Name);
    
            void Read_Parameter(char * File_Name, char * Value_Out);
    
            void List_Dir(const char * Dir_Name, uint8_t levels);
    };
}
#endif

Info_Saver.cpp 文件

#include "Info_Saver.h"

void Info_Saver::Info_Saver::Save_Info(char * File_Name, int Value_In) {
    char Temp_File[40] ="/";
    char End_File[8] = ".csv";
    File Parameter_File;

    strcat(Temp_File, File_Name);
    strcat(Temp_File, End_File); 

    //Serial.println(File_Name);
    //Serial.println(Temp_File);
    Parameter_File = FFat.open(Temp_File,FILE_WRITE);
    if(!Parameter_File){
      Serial.println("- failed to open file for writing");
    } else {
      Parameter_File.print(Value_In);
      Parameter_File.print('\n');
      //Serial.print("The value is: ");
      //Serial.println(Value_In);
    }
    Parameter_File.close();
    strcpy(Temp_File, "/");
    return;
}

void Info_Saver::Info_Saver::Format_Space() { 
    if (!FFat.begin()) {
    FFat.format();
    }
    return;
}

void Info_Saver::Info_Saver::Delete_Parameter(char * File_Name) {
    char Temp_File[40] ="/";
    char End_File[8] = ".csv";
    File Parameter_File;

    strcat(Temp_File, File_Name);
    strcat(Temp_File,End_File); 

    //Serial.println(File_Name);
    //Serial.println(Temp_File);
    if(FFat.remove(Temp_File)) {
        Serial.println("- file deleted");    
    } else {
        Serial.println("- delete failed");
    }
    strcpy(Temp_File, "/");
    return;
}

void Info_Saver::Info_Saver::Read_Parameter(char * File_Name, char * Value_Out) {
    char Temp_File[40] ="/";
    char End_File[8] = ".csv";
    File Parameter_File;

    char Temp_Out;
    strcat(Temp_File, File_Name);
    strcat(Temp_File,End_File);

    //Serial.println(File_Name);
    //Serial.println(Temp_File);
    Parameter_File = FFat.open(Temp_File);
    if(!Parameter_File || Parameter_File.isDirectory()) {
        // add || Parameter_File.isDirectory() if there are issues
        Serial.print("- failed to open file for reading");
        Serial.print("\n");
    } else {
        while(Parameter_File.available()) {
            static unsigned int Temp_Int = 0;
            Temp_Out = Parameter_File.read();
            if (Temp_Out != '\n'  && Temp_Int < 7) {
                Value_Out[Temp_Int] = Temp_Out;
                Temp_Int++;
            } else {
                Value_Out[Temp_Int] = '\0';
                Temp_Int = 0;
                break;
            }
        } 
        //Serial.print("The value is: ");
        //Serial.print(Value_Out);
        //Serial.print("\n");
    }
    Parameter_File.close();
    strcpy(Temp_File, "/");
    return;
}

void Info_Saver::Info_Saver::List_Dir(const char * Dir_Name, uint8_t levels) {
    File Root;
    Root = FFat.open(Dir_Name);
    if (!Root) {
        Serial.println("- failed to open directory");
        return;
    } else if (!Root.isDirectory()) {
        Serial.println(" - not a directory");
        return;
    }

    File Temp_Root = Root.openNextFile();
    while(Temp_Root){
        
        if(Temp_Root.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(Temp_Root.name());
        
            if(levels){
                List_Dir(Temp_Root.path(), levels -1);
            }
        
        } else {
            Serial.print("  FILE: ");
            Serial.print(Temp_Root.name());
            Serial.print("\tSIZE: ");
            Serial.println(Temp_Root.size());
        }
        Temp_Root = Root.openNextFile();
    }
    return;
}

我试过在 .h 文件和 .cpp 文件的开头声明变量。当在 .h 文件中声明变量时,我无法让程序运行。这很容易成为我如何在 .h 文件中声明它们的问题,我只是不确定如何正确地做到这一点。

当我在.cpp文件的开头声明变量时,我意识到变量变成了全局变量。这反过来又对主程序造成了严重破坏。不过,它确实允许我在所有函数中使用变量。

c++ variables libraries
1个回答
0
投票

我认为您在

.h
.cpp
中声明这些变量时遇到的问题是,在这两种情况下,它都使变量成为全局变量(除非您将它们声明为成员变量)。像那样使变量成为全局变量确实会造成破坏。

我会尝试通过重新考虑您的类接口来解决这个问题。我认为

Info_Saver
不应该担心 CSV 文件路径或扩展名,因为这会产生问题:

  1. 用户不知道您需要 CSV 文件,因为这在
    char * File_Name
    参数中并不明显。
  2. 将绝对路径和
    .csv
    附加到
    File_Name
    对于此类的用户来说是完全不可见的,因为它是一个实现细节。
    Info_Saver
    不应该有这个责任(SOLID原则的第一)。
  3. 危险的基于堆栈的溢出,文件名大于 33 个字符(
    /
    +
    .csv
    +
    /
    和空字节需要 7 个字符)。你的
    char Temp_File[40]
    只有这么多空间

因此,您可以改为考虑以下接口:

class Info_Saver {
    #define FORMAT_FFAT true
    public:
        void Save_Info(const char * Csv_File_Path, int Value_In);

        void Format_Space();

        void Delete_Parameter(const char * Csv_File_Path);

        void Read_Parameter(const char * Csv_File_Path, char * Value_Out);

        void List_Dir(const char * Dir_Name, uint8_t levels);
};

通过这些接口更改,现在可以从实现中删除重复的参数修改:

char Temp_File[40] ="/";
char End_File[8] = ".csv";

strcat(Temp_File, File_Name);
strcat(Temp_File, End_File);

这带来了几个好处:

  1. 用户现在知道提供 CSV 文件的有效路径。
  2. 添加绝对路径和
    .csv
    现在是用户的实现细节。如果这很常见,您可以将它实现为一个单独的函数,以便此类的用户可以使用它。
  3. Info_Saver
    再也不用担心提供的文件路径有多大了

此外,您还可以考虑其他一些设计决策:

  • 考虑
    List_Dir
    是否属于
    Info_Saver
    。这看起来更像是一个通用的文件夹列表实用程序,而不是与
    Info_Saver
    .
  • 相关的东西
  • 您可以让每个
    Info_Saver
    对象仅在单个 CSV 文件上工作,从而减少重复参数。否则,几乎不需要类,因为您可以将这些方法实现为自由函数。

例如,在这个设计中,每个方法都作用于

m_Csv_File_Path

class Info_Saver {
    #define FORMAT_FFAT true
    public:
        // Needs implementation
        Info_Saver(const char * Csv_File_Path);

        void Save_Info(int Value_In);
        void Format_Space();

        void Delete_Parameter();
        void Read_Parameter(char * Value_Out);
        
        // Move this somewhere else?
        void List_Dir(const char * Dir_Name, uint8_t levels);
    private:
        const char * m_Csv_File_Path;
};

如果成员变量(

m_Csv_File_Path
)和它们的行为你仍然不知道,前面描述的设计是一个公平的折衷。这种方法需要在调用站点做更多工作,因为您现在需要注意每个 CSV 文件都使用它们自己的
Info_Saver
对象。您还必须注意
m_Csv_File_Path
的生命周期不超过调用者的
Csv_File_Path
生命周期。

© www.soinside.com 2019 - 2024. All rights reserved.