在 cpp 中对象初始化后设置数组的边界

Set the bounds of an array after object initialisation in cpp

我正在使用 C++ 编写图像渲染器,这是我从头开始编写的(我不想使用标准库以外的任何东西),但在尝试存储图像时遇到了一些麻烦。我用来存储图像的 class 看起来像这样:

class RawImage
{
    private:
        RGB pixels[][][3] = {};
    public:
        int width = 0;
        int height = 0;
        RawImage(int width, int height)
        {
            this->width = width;
            this->height = height;
        };
        RGB GetPixel(int x, int y)
        {
            if (x < 0 || x > width - 1)
                return RGB(0.f, 0.f, 0.f);
            if (y < 0 || y > height - 1)
                return RGB(0.f, 0.f, 0.f);
            return pixels[x][y];
        };
        int SetPixel(int x, int y, RGB color)
        {
            if (x < 0 || x > width - 1)
                return -1;
            if (y < 0 || y > height - 1)
                return -1;
            this->pixels[x][y] = color;
            return 0;
        }
};

当我尝试编译这段代码时,g++ 编译器给出了以下错误:

declaration of ‘pixels’ as multidimensional array must have bounds for all dimensions except the first.

如何使用第一个维度大小不同但第三个维度大小固定的多维数组?

Set the bounds of an array after object initialisation in cpp

数组的大小在其生命周期内永远不会改变。它是在创建时设置的。从技术上讲,这对您来说不是问题,因为您可以在构造函数中初始化数组。

但是,数组变量的大小必须是编译时常量,因此您不能接受该大小作为构造函数参数。

您可以使用动态数组。最方便的方法是使用 std::vector.

数组在 C++ 语言中并不是真正的一等公民,multi-dimensional 数组根本不是。没有办法声明一个 multi-dimensional 数组,其中超过第一维的不是编译时常量,句号。基本原理是普通数组是低级对象,只能用于更高级别的容器。不幸的是,由于迭代器的工作方式,构建真正的 multi-level 容器来包装其维度仅在编译时已知的多维数组绝非易事。如果您可以接受,一种简单的方法是使用 operator () 作为访问器方法:pixels(x, y) 而不是 pixels[x][y] 在知道动态维度的容器中。

假设(正如您在评论中确认的那样)您的 RGB 类型是一个 class 或具有三个组件的结构,其构造函数与您的 GetPixel 中使用的形式相同函数,那么你实际上想要一个 2D 数组。但是(正如评论中也提到的那样),通常将位图存储为扁平的 one-dimensional 大小 width × height 的数组更有效。然后可以使用公式 array[y * width + x] 对该数组中的适当元素进行索引(假设 row-major 顺序和 y-ordinates 增加 down 位图)。

你仍然有编译时未知维度的问题,所以你不能使用普通数组。但是 std::vector container 非常适合这个:只需在 RawImage 构造函数中调整它的大小,然后它就可以像普通数组一样使用。另外,当RawImage class的对象被销毁时,所使用的内存会自动释放。

这是您的 class 的可能实现,使用这样的 std::vector:

#include <vector>

class RawImage {
private:
    std::vector<RGB> pixels;
public:
    int width = 0;
    int height = 0;
    RawImage(int width, int height)
    {
        this->width = width;
        this->height = height;
        pixels.resize(width * height);
    };
    RGB GetPixel(int x, int y)
    {
        if (x < 0 || x >= width )
            return RGB(0.f, 0.f, 0.f);
        if (y < 0 || y >= height)
            return RGB(0.f, 0.f, 0.f);
        return pixels[y * width + x];
    };
    int SetPixel(int x, int y, RGB color)
    {
        if (x < 0 || x >= width)
            return -1;
        if (y < 0 || y >= height)
            return -1;
        pixels[y * width + x] = color;
        return 0;
    }
};

重要提示:为了像这样使用std::vector<RGB>容器,RGBclass/structure必须有默认构造函数。我不知道你是如何实现 class 的,但像下面这样的东西会起作用:

struct RGB {
    float r, g, b;
    RGB(float fr, float fg, float fb) : r{ fr }, g{ fg }, b{ fb } { }
    RGB() : r{ 0 }, g{ 0 }, b{ 0 } { } // Default c'tor required by std::vector
};

或者,为简洁起见,您可以 'merge' 通过为每个参数提供默认值,将您的默认构造函数转换为采用三个 float 参数的构造函数:

struct RGB {
    float r, g, b;
    RGB(float fr = 0, float fg = 0, float fb = 0) : r{ fr }, g{ fg }, b{ fb } { }
};