How do I pass an array to a constructor?

55,174

Solution 1

In this case it might be best to use a reference to the array:

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();
};

A couple of advantages - no copying of the array, and the compiler will enforce that the correct size array is passed in.

The drawbacks are that the array you initialize the board object with needs to live at least as long as the object and any changes made to the array outside of the object are 'reflected' into the object's state. but those drawbacks occur if you use a pointer to the original array as well (basically, only copying the array will eliminate those drawbacks).

One additional drawback is that you can't create the object using a pointer to an array element (which is what array function parameters 'decay' to if the array size isn't provided in the parameter's declaration). For example, if the array is passed through a function parameter that's really a pointer, and you want that function to be able to create a board object referring to that array.

Solution 2

Attempting to pass an array to a function results in passing a pointer to the first element of the array.

You can't assign arrays, and taking a parameter like T[] is the same as T*. So

*state = *arr;

Is dereferencing the pointers to state and arr and assigning the first element of arr to the first element of state.

If what you want to do is copy the values from one array to another, you can use std::copy:

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

Alternatively, you should look at std::array<int>, which behaves exactly like you were assuming arrays behave:

#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;
}

Solution 3

#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;
}

or you can use it as:

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

EDIT 1: stable solution

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();
    };
Share:
55,174
Svad Histhana
Author by

Svad Histhana

Updated on July 09, 2022

Comments

  • Svad Histhana
    Svad Histhana almost 2 years

    I want to pass an array to a constructor, but only the first value is passed--the rest looks like garbage.

    Here's a simplified version of what I'm working on:

    #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;
    }
    

    Can someone explain why this doesn't work and how to properly pass an array? Also, I don't want to copy the array. (And do I really have to indent every line by 4 spaces for code? That's pretty tedious.)

  • Svad Histhana
    Svad Histhana about 12 years
    I tried your suggestion and got the error "expression must be a modifiable lvalue" for state.
  • Svad Histhana
    Svad Histhana about 12 years
    Thank you. However, my program won't compile if I try to change *state = *arr; to state = arr; I get an error for state saying "expression must be a modifiable lvalue".
  • vvnraman
    vvnraman about 12 years
    @SvadHisthana I updated the answer as there was a bug in the previous one. I didn't notice that state is declared as int [] which makes it a const pointer and hence its address cannot be changed.
  • Svad Histhana
    Svad Histhana about 12 years
    Thank you. That explains why I'm only getting the first value. I don't want to copy the array, however. Nor do I want to use vectors. Surely there's a way to properly pass an array?
  • Seth Carnegie
    Seth Carnegie about 12 years
    @SvadHisthana nope, you cannot pass a real array by value in C++. You can pass an array by reference though (and that only helps to figure out the size of it) but that's it.
  • Avi
    Avi about 12 years
    @SvadHisthana See the last line, it will do it for you
  • Seth Carnegie
    Seth Carnegie about 12 years
    You can't assign arrays like arrCopy = arrayName
  • Seth Carnegie
    Seth Carnegie about 12 years
    @SvadHisthana remember though that if you do it that way, state will point to the same array as arr and any changes you make to one will be made to the other. Also if arr goes out of scope, state will point to a destroyed array
  • Svad Histhana
    Svad Histhana about 12 years
    Thank you. Though when I tried compiling your first solution, I received an error for line 6: "only static const integral data members can be initialized within a class" vvnraman's solution seems to be working.
  • Rohit Vipin Mathews
    Rohit Vipin Mathews about 12 years
    which is the line? i cant find any assignment of class members outside constructor
  • Al Riddoch
    Al Riddoch about 12 years
    Your first example doesn't compile. You can't initialize a member outside a function using new(). A second issue is that the constructor doesn't copy the contents of the array into the allocated block of memory. It just re-assigns the pointer to point to the array passed into the constructor. This would work in your example code, but in a real program, that memory would become invalid as soon as the array test goes out of scope. The memory allocated and assigned to state would just be leaked.
  • Svad Histhana
    Svad Histhana about 12 years
    Thank you, but I don't want to copy the array. vvnraman's solution seems to be working for me.
  • Svad Histhana
    Svad Histhana about 12 years
    I wasn't intending to pass by value. vvnraman's solution is what I was going for. Sorry if I didn't phrase my question better. Thanks for your help!
  • Rohit Vipin Mathews
    Rohit Vipin Mathews about 12 years
    oh sorry i didnt see it was outside constructor. yeah you can assign memory inside the constructor and copy array contents to avoid out of scope issues.
  • Christian Ammer
    Christian Ammer about 12 years
    @SethCarnegie: You have reversed the std::copy arguments. They are: std::copy(first, last, output).
  • Rohit Vipin Mathews
    Rohit Vipin Mathews about 12 years
    But you might have scope problems in a bigger program. ie if you orginal array goes out of scope
  • Avi
    Avi about 12 years
    Beware. That solution will work in this case. However, test is a local variable and a pointer to it won't be valid after the function returns. Of course when main returns the program is done, but you can run into trouble if test wasn't from main. It's a poor design decision to allow this to happen.
  • Seth Carnegie
    Seth Carnegie about 12 years
    @ChristianAmmer thanks, fixed. Feel free to edit my posts for silly mistakes like that.
  • Svad Histhana
    Svad Histhana about 12 years
    Good to know. That was the desired behavior. However, is there any way to preserve state in case arr does go out of scope?
  • Svad Histhana
    Svad Histhana about 12 years
    This works! Thank you. How does this compare with vvnraman's solution? Also, can you explain what : state(arr) does?
  • vvnraman
    vvnraman about 12 years
    To preserve state, you would need to allocate memory for it (state = new int[64]; and then copy the contents of arr to state (std::copy(arr, arr + 64, state); ). Of course if you are going to do this, you would have to implement the Rule of Three, as now state is a resource.
  • Some programmer dude
    Some programmer dude about 12 years
    @SvadHisthana Just be careful of the drawbacks if you just copy the pointer, as commented by Seth Carnegie on vvnraman's answer.
  • Michael Burr
    Michael Burr about 12 years
    state(arr) initializes the reference to the array that's passed as the constructor parameter.
  • Svad Histhana
    Svad Histhana about 12 years
    @Avram: Is there any way to keep the array alive independently of test without copying it? I guess I don't understand why the array is destroyed when test goes out of scope even if another variable is pointing to the array. Can I just make test point to something else, or does that affect state?
  • Svad Histhana
    Svad Histhana about 12 years
    So there's no way to preserve state without copying the array since the original array declaration is bound to test? I just started learning C++ yesterday, so forgive my naivete. ;)
  • Avi
    Avi about 12 years
    Short answer, no. The thing with a pointer is that it is little more than an int and its value is a memory address. So just like a variable int i in a function is gone when that function ends, so to test is gone when main ends. And just because some pointer has it's address stored test isn't saved any more than i is. If you want to not have it go, then new/delete are the keywords you need to become familiar with.
  • Svad Histhana
    Svad Histhana about 12 years
    Can someone explain why the array is bound to test and is thus destroyed when it goes out of scope? If two variables are pointing to the same array, why does one have precedence over the other?
  • Svad Histhana
    Svad Histhana about 12 years
    Thank you for explaining this. I'll look into new and delete.
  • Michael Burr
    Michael Burr about 12 years
    @Svad: the reference is just an alias for the object it's initialized with. The reference has no effect on that object's lifetime. If you need the board object to live longer than the array used to initialize it, then I'd suggest copying the array (which you indicated several times you didn't want to do). An alternative might be to use shared_ptr to manage the array's lifetime; for that you'd need to create the array in dynamic memory somehow.
  • Svad Histhana
    Svad Histhana about 12 years
    I don't want to copy because I plan to make a very large number of board objects, and copying an array every time would be costly. So it seems I need to allocate memory. Avram mentioned new in another response. I'll also look into shared_ptr. Thanks for the explanation and advice.
  • NathanOliver
    NathanOliver over 7 years
    Do note that you can get rid of arr in your code. Since std::array is an aggregate you can initialize it like you do a regular array. This cuts out a step.