tags:

views:

1722

answers:

9

I am trying to create a class as such:

class CLASS
{
public:
    //stuff
private:
    int x, y;
    char array[x][y];
};

Of course, it doesn't work until I change int x, y; to

const static int x = 10, y = 10;

Which is impractical, because I am trying to read the values of x and y from a file. So is there any way to initialize an array with non-contant values, or declare an array and declare its size on different statements? And I know this would probably require the creation of an array class, but I'm not sure where to start on this, and I don't want to create a 2D dynamic list when the array itself is not dynamic, just the size is not known at compile-time.

+1  A: 

You can't allocate or initialize a global or static array declaratively using non-constant values (compile-time). It's possible for local arrays though (C99 variable sized arrays, as their initializer essentially runs at runtime every time the function is executed).

For your situation, I suggest using a pointer instead of an array and create the actual array dynamically at runtime (using new):

class CLASS
{
public:
    CLASS(int _x, int _y) : x(_x), y(_y) {
       array = new char*[x];
       for(int i = 0; i < x; ++i)
           array[i] = new char[y];
    }
    ~CLASS() {
       for (int i = 0; i < x; ++i)
           delete[] array[i];
       delete[] array;
    }
    //stuff
private:
    int x, y;
    char **array;
};
Mehrdad Afshari
elaborate please?
Keand64
I was talking about allocation and initialization, generally. An array whose size is not known is dynamic. You can't magically allocate memory statically without knowing the amount.
Mehrdad Afshari
That's bad. What about copy construction. Assignment operator. Why not have all the memory in one contiguous block rather than an array of arrays. What about exception handling in the constructor.
Martin York
You're right but I wasn't demonstrating all best practices in implementing an array. I added the code snippet as a response to the requested elaboration about "dynamic array".
Mehrdad Afshari
A: 

If the size is not known at compile time, the array is dynamic. What you could do to keep it static is to make them larger than your largest expected size.

CookieOfFortune
Which will cause a compile error if that size exceeds the space available in a stack frame (usually 4k).
Not Sure
Of course, but he doesn't want to use dynamic memory?
CookieOfFortune
A: 

If you want a dynamically sized array as a class member, you need to array new it and assign that value to a pointer. The char array[size] syntax is only for statically-sized arrays.

Better yet, you really should use an std::vector< std::vector<char> >, there are very few good reasons to manually work with dynamically sized arrays these days.

Not Sure
+12  A: 

use vector.

#include <vector>
class YourClass
{
public:
    YourClass()
    : x(read_x_from_file()), y(read_y_from_file())
    {
        my_array.resize(x);
        for(int ix = 0; ix < x; ++ix)
            my_array[ix].resize(y);
    }

    //stuff

private:
    int x, y;
    std::vector<std::vector<char> > my_array;
};
Aaron
Good as a "strictly standard" solution, but surely `boost::multi_array` also deserves a mention?
Pavel Minaev
You can do all inline without a loop: `: x(read_x_from_file()), y(read_y_from_file()), my_array(x, vector<char>(y)) {}`. That said, i would do both the file-reading and the allocation in the body of the ctor. I try to keep the side-effects done by a ctor-initializer to an absolute minimal amount.
Johannes Schaub - litb
+3  A: 

Not in that manner, as in c++, c-style array sizes have to be known at compile time, with some vendor specific extensions allowing certain runtime sizes (to enhance compatibility with C99), but not in the situation you are describing (if you are interested, here's a description). The easiest thing to do would be:

std::vector< std::vector<char> > array;

And apply the size in the constructor:

array.resize(x);
for(std::vector< std::vector<char> >::iterator curr(array.begin()),end(array.end());curr!=end;++curr){
   curr->resize(y);
}

There are many advantages of vector over c style arrays, see here

Todd Gardner
+2  A: 

The compiler need to have the exact size of the class when compiling, you will have to use the new operator to dynamically allocate memory.

Switch char array[x][y]; to char** array; and initialize your array in the constructor, and don't forget to delete your array in the destructor.

class MyClass
{
public:
    MyClass() {
        x = 10; //read from file
        y = 10; //read from file
        allocate(x, y);
    }

    MyClass( const MyClass& otherClass ) {
        x = otherClass.x;
        y = otherClass.y;
        allocate(x, y);

        // This can be replace by a memcopy
        for( int i=0 ; i<x ; ++i )
            for( int j=0 ; j<x ; ++j )
                array[i][j] = otherClass.array[i][j];
    }

    ~MyClass(){
        deleteMe();
    }

    void allocate( int x, int y){
        array = new char*[x];
        for( int i = 0; i < y; i++ )
            array[i] = new char[y];
    }

    void deleteMe(){
        for (int i = 0; i < y; i++)
           delete[] array[i];
        delete[] array;
    }

    MyClass& operator= (const MyClass& otherClass)
    {
        if( this != &otherClass )
        {
            deleteMe();
            x = otherClass.x;
            y = otherClass.y;
            allocate(x, y);
            for( int i=0 ; i<x ; ++i )
                for( int j=0 ; j<y ; ++j )
                    array[i][j] = otherClass.array[i][j];            
        }
        return *this;
    }
private:
    int x, y;
    char** array;
};

*EDIT: I've had the copy constructor and the assignment operator

Steve Gury
and to access a particular elemtent i could say array[xpos][ypos];?
Keand64
Yes Keand64, simply use array[xpos][ypos];
Steve Gury
That's bad. What about copy construction. Assignment operator. Why not have all the memory in one contiguous block rather than an array of arrays. What about exception handling in the constructor.
Martin York
To access element [xPos][yPos] you need to write a method that accepts two parameters and returns a reference to the correct element in the array member.: char}
Martin York
Martin, you're right, I've taken your comment into account. Personnaly I definitely would have used a vector (as aaron mentionned), but as the title of the question is "Is there a way to initialize an array with non-constant variables?", I've written an answer corresponding to this question.
Steve Gury
Better. If you throw in the constructor you leak all allocated memory. The assignment operator is not guranteed to work. What happens if the two arrays have different dimensions. It would be better to make it private.
Martin York
Not to be competitive or anything, having posted a competing answer, but you still haven't fixed the memory leaks here nor the buffer overrun in your assignment operator. Downvote. Will gladly reverse into an upvote when the code works reliably.
Aaron
This is an ideal scenario for copy/swap :) could greatly reduce code lines
Johannes Schaub - litb
+2  A: 

Put all the memory into one block.
Because it is private you can then get your access methods to retrieve the correct value.

Quick example:

#include <vector>
#include <iostream>

class Matrix
{
    public:
    class Row
    {
        public:
        Row(Matrix& p,unsigned int x)
            :parent(p)
            ,xAxis(x)
        {}
        char& operator[](int yAxis)
        {
            return parent.data(xAxis,yAxis);
        }
        private:
            Matrix&         parent;
            unsigned int    xAxis;
    };

    Matrix(unsigned int x,unsigned int y)
        :xSize(x)
        ,ySize(y)
        ,dataArray(x*y)
    {}

    Matrix::Row operator[](unsigned int xAxis)
    {
        return Row(*this,xAxis);
    }
    char& data(unsigned int xAxis,unsigned int yAxis)
    {
        return dataArray[yAxis*xSize + xAxis];
    }
    private:
        unsigned int xSize;
        unsigned int ySize;
        std::vector<char>   dataArray;
};


int main()
{
    Matrix      two(2,2);

    two[0][0]   = '1';
    two[0][1]   = '2';
    two[1][0]   = '3';
    two[1][1]   = '4';

    std::cout <<  two[1][0] << "\n";
    std::cout <<  two.data(1,0) << "\n";
}
Martin York
Nice one. I'll have to remember that trick.
Luis
+1  A: 

Take a look at boost::multi_array.

Steve Jessop
+1  A: 

You can allocate memory to your 2-dimensional array in the constructor and free it in the destructor. The simplest way:

array = (char **)malloc(sizeof(char *) * x);
if (array) {
    for (i = 0; i < x; i++) {
        array[i] = (char *)malloc(sizeof(char) * y);
        assert(array[i]);
    }
}