如何将数组传递给构造函数?

21

我想将一个数组传递给构造函数,但只有第一个值被传递了——其余的看起来像垃圾。

这是我正在处理的简化版本:

#include <iostream>

class board
{
    public:
        int state[64];
        board(int arr[])
        {
            *state = *arr;
        }
        void print();
};

void board::print()
{
    for (int y=0; y<8; y++)
    {
        for (int x=0; x<8; x++)
            std::cout << state[x + y*8] << " ";
        std::cout << "\n";
    }
}

int main()
{
    int test[64] = {
        0, 1, 2, 3, 4, 5, 6, 7,
        1, 2, 3, 4, 5, 6, 7, 8,
        2, 3, 4, 5, 6, 7, 8, 9,
        3, 4, 5, 6, 7, 8, 9,10,
        4, 5, 6, 7, 8, 9,10,11,
        5, 6, 7, 8, 9,10,11,12,
        6, 7, 8, 9,10,11,12,13,
        7, 8, 9,10,11,12,13,14 };

    board b(test);
    b.print();

    std::cin.get();
    return 0;
}

能否有人解释一下为什么这个方法不起作用,以及如何正确地传递一个数组?此外,我不想复制这个数组。(还有,我真的必须对每行代码缩进4个空格吗?这太繁琐了。)


int (arr&)[64] 是您的数组类型,而数组无法复制。最简单的方法是使用 std::array。 - Mooing Duck
关于缩进,IDE通常会自动完成。 - default
关于缩进 - 您不需要手动为每行单独进行缩进。选择代码并按编辑框上方带有一对大括号 - {} - 的按钮。 - Michael Burr
@Default:是的,我意识到我只需要进入我的IDE,全选,按Tab键,然后就可以为这个网站格式化所有内容了。我真傻。 - Svad Histhana
@Michael Burr:谢谢。下次我会尝试那种方法。 - Svad Histhana
6个回答

10

在这种情况下,最好使用对该数组的引用:

class board
{
    int (&state)[64];

public:
    board(int (&arr)[64]) 
        : state(arr)
    {}

    // initialize use a pointer to an array
    board(int (*p)[64]) 
        : state(*p)
    {}


    void print();
};
几个优点——不需要复制数组,编译器将强制传递正确大小的数组。缺点是你需要用初始化board对象的数组至少与对象同生共死,并且在对象外更改数组会“反映”到对象状态中。但如果使用指针到原始数组仍会出现这些问题(基本上只有复制数组才能消除这些缺点)。另一个缺点是你不能使用指向数组元素的指针创建对象(如果函数参数声明中没有提供数组大小,则数组函数参数会“衰减”为指向数组元素的指针)。例如,如果通过指针传递数组,而函数实际上是指针,则希望该函数能够创建引用该数组的board对象。

这个可行!谢谢。这个和vvnraman的解决方案相比如何?此外,您能解释一下 : state(arr) 的作用吗? - Svad Histhana
state(arr) 初始化作为构造函数参数传递的数组的引用。 - Michael Burr
有人能解释一下为什么数组绑定到了 test,因此当它超出作用域时被销毁吗?如果两个变量指向同一个数组,为什么其中一个优先于另一个? - Svad Histhana
1
@Svad:引用只是一个对象的别名,它对该对象的生命周期没有影响。如果您需要board对象的寿命比用于初始化它的数组更长,则建议复制该数组(您已经多次表示不想这样做)。另一种选择可能是使用shared_ptr来管理数组的生命周期;为此,您需要以某种方式在动态内存中创建数组。 - Michael Burr
我不想复制,因为我打算制作大量的板对象,每次复制一个数组会很昂贵。因此,我需要分配内存。Avram在另一个回答中提到了“new”。我还会研究“shared_ptr”。感谢你的解释和建议。 - Svad Histhana

8
尝试将数组传递给函数会导致传递指向数组第一个元素的指针。不能分配数组,采用像 T[] 这样的参数与 T* 相同。因此,应该这样:
*state = *arr;

将指向statearr的指针取消引用,并将arr的第一个元素赋值给state的第一个元素。

如果您想要复制一个数组的值到另一个数组,您可以使用std::copy

std::copy(arr, arr + 64, state); // this assumes that the array size will
                                 // ALWAYS be 64

另外,您可以看一下 std :: array<int>,它的行为与您假定的数组完全相同:

#include <array>
#include <algorithm>
#include <iostream> 

class board
{
    public:
        std::array<int, 64> state;

        board(const std::array<int, 64> arr) // or initialiser list : state(arr)
        {
            state = arr; // we can assign std::arrays
        }
        void print();
};

void board::print()
{
    for (int y=0; y<8; y++)
    {
        for (int x=0; x<8; x++)
            std::cout << state[x + y*8] << " ";
        std::cout << "\n";
    }
}

int main()
{
    // using this array to initialise the std::array 'test' below
    int arr[] = {
        0, 1, 2, 3, 4, 5, 6, 7,
        1, 2, 3, 4, 5, 6, 7, 8,
        2, 3, 4, 5, 6, 7, 8, 9,
        3, 4, 5, 6, 7, 8, 9,10,
        4, 5, 6, 7, 8, 9,10,11,
        5, 6, 7, 8, 9,10,11,12,
        6, 7, 8, 9,10,11,12,13,
        7, 8, 9,10,11,12,13,14 };

    std::array<int, 64> test(std::begin(arr), std::end(arr));

    board b(test);
    b.print();

    std::cin.get();
    return 0;
}

谢谢。这解释了为什么我只得到第一个值。然而,我不想复制数组。我也不想使用向量。肯定有一种正确的方法来传递一个数组? - Svad Histhana
3
不可以在 C++ 中按值传递真正的数组,但可以通过引用传递数组(这只有助于确定其大小),但也仅限如此。 - Seth Carnegie
我并不打算通过值传递。vvnraman的解决方案正是我想要的。如果我的问题没有表达清楚,对不起。感谢您的帮助! - Svad Histhana
@SethCarnegie:你颠倒了std::copy的参数。正确顺序是:std::copy(first, last, output) - Christian Ammer
@ChristianAmmer 谢谢,已修复。如果有类似的低级错误,请随意编辑我的帖子。 - Seth Carnegie
请注意,您可以在代码中摆脱 arr。由于 std::array 是一个聚合体,您可以像初始化常规数组一样对其进行初始化。这样可以省略一步。 - NathanOliver

1
#include <iostream>

class board
{
    public:
        int * state;    //changed here, you can also use **state
        board(int *arr)               //changed here
        {
          state = arr;
        }
        void print();
};

void board::print()
{
    for (int y=0; y<8; y++)
    {
        for (int x=0; x<8; x++)
            std::cout << *(state + x + y*8) << " ";   //changed here
        std::cout << "\n";
    }
}

int main()
{
    int test[64] = {
        0, 1, 2, 3, 4, 5, 6, 7,
        1, 2, 3, 4, 5, 6, 7, 8,
        2, 3, 4, 5, 6, 7, 8, 9,
        3, 4, 5, 6, 7, 8, 9,10,
        4, 5, 6, 7, 8, 9,10,11,
        5, 6, 7, 8, 9,10,11,12,
        6, 7, 8, 9,10,11,12,13,
        7, 8, 9,10,11,12,13,14 };

    board b(test);
    b.print();

    std::cin.get();
    return 0;
}

或者你可以这样使用:

class board
{
    public:
        int state[64];
        board(int arr[])
        {
            for(int i=0;i<64;++i)
               state[i] = arr[i];
        }
        void print();
};

编辑 1: 稳定解决方案

class board
    {
        public:
            int * state;    //changed here, you can also use **state
            board(int *arr)               //changed here
            {
              state = new int[64];
              for(int i=0;i<64;++i)
                   state[i] = arr[i];
            }
            void print();
    };

谢谢。虽然我尝试编译了你的第一个解决方案,但是我收到了第6行的错误提示:“只有静态常量整型数据成员可以在类内初始化”。vvnraman的解决方案似乎有效。 - Svad Histhana
哪一行代码?我在构造函数外找不到类成员的任何赋值。 - Rohit Vipin Mathews
1
你的第一个示例无法编译。你不能使用new()在函数外初始化成员。第二个问题是构造函数没有将数组内容复制到分配的内存块中,它只是重新分配指针以指向传递到构造函数中的数组。这在你的示例代码中可以工作,但在实际程序中,一旦test数组超出范围,该内存将变为无效状态。分配并赋值给state的内存将会被泄漏。 - Al Riddoch
哦,抱歉我没看到它在构造函数外面。是的,你可以在构造函数内分配内存并复制数组内容以避免超出作用域的问题。 - Rohit Vipin Mathews

0

*state = *arr;使用解引用,它返回指针地址处的值。

这与state[0] = *arr;相同,因为*arr是一个int

有关指针的信息,请参阅本文。请查看解引用部分。

要解决这个问题,您需要执行以下操作:

for (int i = 0; i < 64; i++) state[i] = arr[i]

@SvadHisthana 看最后一行,它会为你完成。 - Avi
谢谢,但我不想复制这个数组。vvnraman的解决方案对我来说似乎是有效的。 - Svad Histhana
注意。那个解决方案在这种情况下可以工作。但是,test是一个局部变量,指向它的指针在函数返回后将无效。当然,当main返回时程序已经完成,但如果test不是来自main,你可能会遇到麻烦。允许这种情况发生是一个糟糕的设计决策。 - Avi
@SvadHisthana 如果你只是复制指针,要小心缺点,就像Seth Carnegie在vvnraman的答案评论中所说。 - Some programmer dude
简短的回答是不行的。指针的问题在于它只不过是一个int,其值是一个内存地址。所以就像函数中的变量int i在函数结束时消失一样,测试也会在main结束时消失。并且仅仅因为某个指针有它的地址存储,测试并没有被保存,就像i一样。如果你想让它不消失,那么new/delete是你需要熟悉的关键字。 - Avi
显示剩余4条评论

0
*arr 给出存储在 arr[0] 中的值。在 C++ 中,数组的名称是指向数组中第一个元素的指针。
因此,当您执行 *state = *arr 时,您将 arr[0] 处的值存储在变量 state 中。
现在,如果您想传递数组而不必显式复制每个元素,我建议您在调用方法中创建另一个相同大小的数组,然后将调用者的数组名称传递给该方法,本质上是:
methodWhereArrayisPassed(int *arrayName)
{
    int arrCopy[64];
    arrCopy = arrayName;

// Do more stuff here
}

methodWhichPassesArray()
{
    // do stuff here
    int arr[] = {
       0, 1, 2, 3, 4, 5, 6, 7,
       1, 2, 3, 4, 5, 6, 7, 8,
       2, 3, 4, 5, 6, 7, 8, 9,
       3, 4, 5, 6, 7, 8, 9,10,
       4, 5, 6, 7, 8, 9,10,11,
       5, 6, 7, 8, 9,10,11,12,
       6, 7, 8, 9,10,11,12,13,
       7, 8, 9,10,11,12,13,14 };

methodWhereArrayisPassed(arr);

// do stuff here
}

2
你不能像这样分配数组:arrCopy = arrayName - Seth Carnegie

0

数组的名称是它第一个元素的地址。

因此,行 *state = *arr 会将state[0]设置为arr[0]

由于你现在定义了int state[64];stateconst指针类型的int,其地址不能被改变。

您可以将其更改为int *state; 然后state = arr 将起作用。


我尝试了你的建议,但是针对state出现了“表达式必须是可修改的左值”的错误。 - Svad Histhana
@SvadHisthana 我更新了答案,因为之前的有一个错误。我没有注意到 state 被声明为 int [],这使它成为一个 const 指针,因此其地址无法更改。 - vvnraman
2
@SvadHisthana 请记住,如果您这样做,state 将指向与 arr 相同的数组,对其中一个所做的任何更改都将应用于另一个。此外,如果 arr 超出范围,state 将指向已销毁的数组。 - Seth Carnegie
好的,知道了。那是期望的行为。然而,如果arr超出作用域,有没有办法保留state呢? - Svad Histhana
为了保留 state,您需要为其分配内存 (state = new int[64];),然后将 arr 的内容复制到state (std::copy(arr, arr + 64, state);). 当然,如果您要这样做,您必须实现 Rule of Three,因为现在的 state 是一种资源。 - vvnraman
所以,如果原始数组声明绑定到“test”,那么没有办法保留“state”而不复制数组吗?我昨天刚开始学习C ++,请原谅我的幼稚。;) - Svad Histhana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接