【问题标题】:C++ Declaring arrays in class and declaring 2d arrays in classC++ 在类中声明数组并在类中声明二维数组
【发布时间】:2016-12-09 00:22:42
【问题描述】:

我是使用类的新手,在将数组放入类中时遇到了问题。我想为限制为 50 个字符的文本初始化一个 char 数组,然后用函数替换文本。

#ifndef MAP_H
#define MAP_H
#include "Sprite.h"
#include <SFML/Graphics.hpp> 
#include <iostream>

class Map : public sprite
{
private:
    char mapname[50];
    int columnnumber;
    int linenumber;
    char casestatematricia[];

public:
    void setmapname(char newmapname[50]);
    void battlespace(int column, int line);
    void setcasevalue(int col, int line, char value);
    void printcasematricia();

};


#endif

顺便说一句,我可以像这样初始化我的二维数组

char casestatematricia[][];

我想稍后让这个二维数组动态化,我输入一个列号和一个类似的行号

casestatematricia[linenumber][columnnumber]

创建一个战场。

这是 cpp 代码,以便您了解我想要做什么。

#include "Map.h"
#include <SFML/Graphics.hpp> 
#include <iostream>

using namespace sf;

void Map::setmapname(char newmapname[50])
{
    this->mapname = newmapname;
}
void Map::battlespace(int column, int line)
{

}
void Map::setcasevalue(int col, int line, char value)
{

}
void Map::printcasematricia()
{

}

提前谢谢你。

【问题讨论】:

  • 看看std::string 替换包含字符串的char 数组。另外,请查看std::vectorstd::liststd::map、...(即标准容器)而不是使用原始容器。
  • @Garf365 我不使用 char[50] 无缘无故,这个数据必须在以后序列化,所以它必须有一个固定的权重。
  • 我也有需要序列化的数据,它们与std::string 无关。因为你有一些设置器,只需在大小上添加条件
  • @PyrrhaDaSmash4Player “固定重量”是什么意思? std::vectors 数据是连续存储的,也就是说,您可以在需要时随时从其中获取 c 样式的数组。
  • 如果您正准备将整个类内容写入文件,就好像它是一个字节数组一样,这通常不是一个好主意。它是极其不可移植的,一旦你尝试使用 rtti、虚函数或任何其他非 POD 的东西,它就会咬你一口。 @Garf365 给你很好的建议;你应该听他的。

标签: c++ arrays class multidimensional-array sfml


【解决方案1】:

考虑在这方面遵循常见做法。 大多数(例如数字)库不在类中使用二维数组。 它们使用动态分配的 1D 数组并重载 () 或 [] 运算符以类似 2D 的方式访问正确的元素。 所以在外面你永远不能说你实际上是在处理连续存储,它看起来像一个二维数组。 通过这种方式,数组更容易调整大小,存储、转置和重塑的效率更高。

【讨论】:

    【解决方案2】:

    只是针对您的问题提出建议:

    class Map : public sprite
    {
    private:
        std::string mapname;
        int columnnumber;
        int linenumber;
        std::vector<char> casestatematricia;
    
        static constexpr std::size_t maxRow = 50;
        static constexpr std::size_t maxCol = 50; 
    
    public:
        Map():
            casestatematricia(maxRow * maxCol, 0)
        {}
        void setmapname(std::string newmapname)
        {
            if (newmapname.size() > 50)
            {
                // Manage error if you really need no more 50 characters..
                // Or just troncate when you serialize!
            }
            mapname = newmapname;
        }
    
        void battlespace(int col, int row);
        void setcasevalue(int col, int row, char value)
        {
            // check that col and line are between 0 and max{Row|Column} - 1
            casestatematricia[row * maxRow + col] = value;
        }
    
        void printcasematricia()
        {
            for (std::size_t row = 0; row < maxRow; ++row)
            {
                for (std::size_t col = 0; col < maxCol; ++col)
                {
                    char currentCell = casestatematricia[row * maxRow + col];
                }
            }
        }
    };
    

    要像访问二维数组一样访问一维数组,请查看Access a 1D array as a 2D array in C++

    当您考虑序列化时,我猜您想将其保存到文件中。只是一个建议:不要将原始内存存储到文件中,只是为了“节省”重新启动软件的时间。您只有一个非便携式解决方案!说真的,有了计算机的强大功能,您不必担心从文件加载的时间!

    我建议你在你的类中添加 2 个方法来将 Map 保存到文件中

    void dump(std::ostream &os)
    {
        os << mapname << "\n";
        std::size_t currentRow = 0;
        for(auto c: casestatematricia)
        {
            os << static_cast<int>(c) << " ";
            ++currentRow;
    
            if (currentRow >= maxRow)
            {
                currentRow = 0;
                os << "\n";
            }
        }
    }
    
    void load(std::istream &is)
    {
        std::string line;
    
        std::getline(is, line);
        mapname = line;
    
        std::size_t current_cell = 0;
        while(std::getline(is, line))
        {
            std::istringstream is(line);
            while(!is.eof())
            {
                char c;
    
                is >> c;
                casestatematricia[current_cell] = c;
    
                ++current_cell;
            }
        }
    }
    

    此解决方案仅作为示例。他们不管理错误,我选择将其以 ASCII 格式存储在文件中。您可以更改为以二进制形式存储,但不要使用直接写入原始内存。你可以看看C - serialization techniques(只需要翻译成C++)。但请不要使用memcpy 或类似技术进行序列化

    【讨论】:

    • thx 我会尝试做类似的事情,我已经完成了序列化,所以我不需要那个 thx :3
    【解决方案3】:

    我希望我做对了。你有两个问题。您想知道如何通过void setmapname(char newmapname[50]); 分配char mapname[50]; 的值。你想知道如何创建一个动态大小的二维数组。

    我希望您对指针感到满意,因为在这两种情况下,您都需要它。

    对于第一个问题,我先纠正你对void setmapname(char newmapname[50]);的理解。 C++ 函数不接受数组。它接受指向数组的指针。所以写void setmapname(char *newmapname);就好了。为了更好的理解,请转至Passing Arrays to Function in C++

    这样,我将更改函数以读取新地图名称的长度。要分配mapname,只需使用循环复制每个字符。

    void setmapname(char *newmapname, int length) {
        // ensure that the string passing in is not 
        // more that what mapname can hold.
        length = length < 50 ? length : 50;
    
        // loop each value and assign one by one. 
        for(int i = 0; i < length; ++i) {
            mapname[i] = newmapname[i];
        }
    }
    

    对于第二个问题,你可以像Garf365提出的那样使用vector需要使用,但我更喜欢只使用指针,我将使用一维数组来表示二维战场。 (您可以阅读 Garf365 提供的链接)。

    // Declare like this
    char *casestatematricia; // remember to initialize this to 0.
    
    // Create the battlefield
    void Map::battlespace(int column, int line) {
    
        columnnumber = column;
        linenumber = line;
    
        // Clear the previous battlefield.
        clearspace();
    
        // Creating the battlefield
        casestatematricia = new char[column * line];
    
        // initialise casestatematricia...
    }
    
    // Call this after you done using the battlefield
    void Map::clearspace() {
        if (!casestatematricia) return;
    
        delete [] casestatematricia;
        casestatematricia = 0;
    }
    

    当您不再使用它时,请记得致电clearspace()

    为了您的利益,这就是您创建动态大小的二维数组的方式

    // Declare like this
    char **casestatematricia; // remember to initialize this to 0.
    
    // Create the battlefield
    void Map::battlespace(int column, int line) {
    
        columnnumber = column;
        linenumber = line;
    
        // Clear the previous battlefield.
        clearspace();
    
        // Creating the battlefield
        casestatematricia = new char*[column];
        for (int i = 0; i < column; ++i) {
            casestatematricia[i] = new char[line];
        }
    
        // initialise casestatematricia...
    }
    
    // Call this after you done using the battlefield
    void Map::clearspace() {
        if (!casestatematricia) return;
    
        for(int i = 0; i < columnnumber; ++i) {
            delete [] casestatematricia[i];
        }
    
        delete [][] casestatematricia;
        casestatematricia = 0;
    } 
    

    希望对您有所帮助。

    PS:如果需要对字符串进行序列化,可以使用pascal字符串格式,这样就可以支持变长字符串。例如“11hello world”或“3foo”。

    【讨论】:

    • 一些批评(我希望是正面的):(1)对于setmapname,不要使用self maid循环,你有一些标准函数可以将数组复制到另一个数组中(改用std::copy , std::memcpystd::strncpy) => 始终使用标准功能而不是家庭佣工;) ; (2) 避免使用具有所有权和内存泄漏风险等问题的原始指针,使用智能指针(c++11 起,或使用 boost); (3) 使用容器而不是自己管理内存 => 总的来说,更喜欢使用许多人开发和审查的标准,而不是一个人编写的代码。
    • 例如,std::vector 执行您手动执行的操作 => 分配连续内存,释放它,管理访问(如果您使用 vectorat 方法,如果您给出一个超出范围的值,比未定义的行为更安全),并且有更多的东西,比如增加内存(realloc),......但它确实有效,很多人共同努力实现这一目标
    • @Garf365,我同意你的看法。使代码可维护是我们程序员的必经之路。但是,我的印象是OP还在学习c++,学习者。所以我构建我的答案的方式是希望你得到正确的基本理解,并知道如何在没有图书馆的情况下做到这一点。知道怎么做之后。然后无论如何使用任何资源,stl或boost都没关系。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-15
    • 2019-12-11
    • 1970-01-01
    相关资源
    最近更新 更多