如何在C++构造函数中初始化类成员变量char[][]



如何声明和创建一个二维数组,该数组的维度在编译时是常量和已知的,但由拥有它的类的构造函数的字面const参数指定?

例如。。。

食品:

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

我知道buf(char[rows][cols])完全错了。。。它只是为了说明我正在努力实现的目标。

我确信我必须以某种方式使用构造函数初始化语法(就像我用来设置totalRowstotalCols的值一样(。。。但是,对于数组,我不确定实际上是什么语法。我不确定这样的语法是否存在用于数组声明,因为";const-int参数的构造函数,该构造函数只能用文字调用以保证值在编译时是已知的,因此适用于数组声明";无可否认,这是一种极端边缘的情况。

在Java中,我只需要声明一个名为bufchar[],它的值在声明时被隐式取消赋值,然后在构造函数的主体中创建(并赋值(它。。。但是,据我所知,C++也不允许那个

我知道我可能可以通过将buf设置为char*并通过malloc(rows * cols)在构造函数中创建它来回避这个问题。。。但这似乎有点野蛮,而且似乎";坏的";由于某种原因,我无法完全理解。


更新

好吧,看起来Adrian Mole使用模板的想法是正确的,但现在我有了一个新问题。。。因为Foo现在是Foo<4,20>,编译器不允许我将FooFoo*指定为方法或构造函数参数类型。。。它也希望显式指定它们

尽管如此,我刚刚发现Foo.cpp现在有一个更大的问题。当我尝试实现Foo其他方法(我想我应该已经提到了(时,编译器拒绝了它们,因为";Foo不是一个类、命名空间或声明;。例如,以下行现在被拒绝:

int Foo::getTotalRows() {
return totalRows;
}

大概是因为不再存在类Foo。除了,看起来我甚至不能通过插入<4,20>(例如int Foo<4,20>::getTotalRows()(。

new Main.cpp:

Foo<4,20> myFoo;
Bar firstBar = new Bar(&myFoo, 2,17,4);

条形图h:

class Bar {
public:
Bar(Foo<4,20>* srcFoo, int row, int col, int len);
// ... snip ...
private:
char* chars;
int length;
}

Bar.cpp:

{
Bar::Bar(Foo<4,20>* srcFoo, int row, int startCol, int length) 
: chars(*srcFoo->getBuf(row, startcol, length), length(length) {}
// ... snip ...
}

这有点违背了这一点,因为这意味着我从必须在一个地方对4和20进行硬编码,到必须在每个地方对其进行硬编码我随后使用了Foo对象。

除非。。。有一种方法可以向编译器指示,方法/构造函数应该与模板化类的任何变体相匹配,并让编译器在编译时自动生成特定于风格。例如,类似。。。

Bar(Foo<>* pFoo, int row, int col, int len);

因此,如果Main.cpp执行以下操作:

Foo<4,20> myFoo;
Bar first(&myFoo,1,17,3);

编译器会说;好吧,我知道myFoo是一个Foo<4,20>,他正在给巴尔的建造师打电话。Bar的构造函数采用Foo<gt;,所以我会假装他在来源中声明为:

Bar(Foo<4,20>* pFoo, int row, int col, int len);

并正常继续。然后,再往下看,如果我这样做了:

Foo<2,16> smallerFoo;
Bar second(&smallerFoo, 0,3,11);

编译器会说;好的,smallerFoo是Foo<2,16>BarFoo*采用构造函数匹配,但由于我们还没有使用Foo<2,16>,我将不得不自动生成另一组方法,并假装源真的说了类似于的话

Bar(Foo<4,20>* pFoo, int row, int col, int len);
Bar(Foo<2,16>* pFoo, int row, int col, int len);

换句话说,让编译器递归地将模板应用于自动生成匹配的方法

是否存在类似的东西,或者除非我想明确指定<行,列>在Foo对象(包括方法参数(的每次后续使用中?

您可以将Foo作为类模板,然后编译器将为所使用的rowscols的每个不同组合创建一个单独的类。这将避免在构造函数中进行任何初始化(对于您所概述的简单类(,因为所有数据成员都可以默认初始化为它们所需的值。

main中的明显区别是,您将使用Foo<4, 20> myFoo…,而不是像Foo myFoo(4, 20);那样使用语法声明和初始化,它将创建myFoo作为名为(类似(Foo<4,20>的类的对象。

这里有一个简短的概述,其中包括一些示例成员函数,以及如何为这样一个类模板声明/定义它们:

#include <iostream>
template<int rows, int cols>
class Foo {
public:
Foo() = default;
void Init();
void List();
private:
int totalRows{ rows };
int totalCols{ cols };
char buf[rows][cols]{};
};
template<int rows, int cols>
void Foo<rows, cols>::Init()
{
char i = 0;
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
buf[r][c] = i++;
}
}
}
template<int rows, int cols>
void Foo<rows, cols>::List()
{
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
std::cout << static_cast<int>(buf[r][c]) << " ";
}
std::cout << std::endl;
}
}
int main()
{
Foo<4, 20> myFoo;
myFoo.Init();
myFoo.List();
return 0;
}

使用向量中的一个向量。

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

最新更新