Home > Back-end >  How to initialize class member variable char[][] in C constructor?
How to initialize class member variable char[][] in C constructor?

Time:07-24

How do you declare and create a 2-dimensional array whose dimensions are constant and known at compile time, but are specified by literal const arguments to the constructor of the class that owns it?

For example...

Foo.h:

class Foo {
public:
    Foo(int rows, int cols);
private:
    int totalRows;
    int totalCols;
    char buf[4][20]; // I don't actually WANT to hardcode 4 and 20 here!
};

Foo.cpp:

Foo::Foo(const int rows, const int cols) : totalRows(rows), totalCols(cols), buf(new char[rows][cols]) {}

Main.cpp:

Foo myFoo(4,20);

I know buf(char[rows][cols]) is totally wrong... it's just there to illustrate what I'm trying to achieve.

I'm pretty sure I somehow have to use constructor-initialization syntax (like I'm using to set the values of totalRows and totalCols) ... but, for arrays, I'm not sure what that syntax actually is. I'm not sure whether such syntax even exists for array-declaration, since the use case of "constructor with const int args that can only be invoked with literals to guarantee the values are known at compile-time and thus suitable for an array declaration" is admittedly kind of an extreme edge case.

In Java, I'd just declare a char[] named buf whose value is implicitly unassigned at declaration-time, then create (and assign) it in the body of the constructor ... but, as far as I know, C doesn't allow that, either.

I know I could probably sidestep the issue by making buf a char* and creating it in the constructor via malloc(rows * cols) ... but that seems kind of barbaric and just seems "bad" for some reason I can't quite put my finger on.

CodePudding user response:

You could make Foo a class template, then a separate class will be created by the compiler for each different combination of rows and cols that are used. This would avoid the need for any initialization in the constructor (for the simple class that your have outlined), as all data members could be default initialized to their desired values.

The visible difference in your main would be, rather than declaring and initializing with syntax like Foo myFoo(4, 20);, you would use Foo<4, 20> myFoo … which would create myFoo as an object of the class called (something like) Foo<4,20>.

Here's a short outline:

#include <iostream>

template<int rows, int cols>
class Foo {
public:
    Foo() {}
private:
    int totalRows{ rows };
    int totalCols{ cols };
    char buf[rows][cols];
};

int main()
{
    Foo<4, 20> myFoo;
    //...
}

CodePudding user response:

Use a vector of vectors.

#include <cassert>
#include <vector>
#include <stdexcept>

class Foo
{
public:
    // with std::vector you can dynamically allocate
    // a 2D array of the size you want.

    Foo(std::size_t rows, std::size_t cols) :
        m_buffer(rows, std::vector<int>(cols, 0)) // initialize all rows to 0
    {
        if ((rows == 0) || (cols == 0)) throw std::invalid_argument("invalid input size");
    }

    std::size_t number_of_rows() const
    {
        return m_buffer.size();
    }

    std::size_t number_of_columns() const
    {
        return m_buffer[0].size();
    }

    int at(std::size_t row, std::size_t column)
    {
        if ((row >= number_of_rows()) || (column >= number_of_columns())) throw std::invalid_argument("invalid index");
        return m_buffer[row][column];
    }

private:
    std::vector<std::vector<int>> m_buffer;
};


int main()
{
    Foo foo{ 3,4 };
    assert(foo.number_of_rows() == 3ul);
    assert(foo.number_of_columns() == 4ul);
    assert(foo.at(1, 1) == 0);

    return 0;
}
  • Related