从函数中返回多维数组

25

如何返回存储在我的类的private字段中的多维数组?

class Myclass {
private:
   int myarray[5][5];
public:
   int **get_array();
};

// This does not work:
int **Myclass::get_array() {
    return myarray;
}

我遇到了以下错误:

无法将int (*)[5][5]转换为int **并返回


4
什么是网格?难道不应该是myarray吗? - woodstok
3
直接返回私有成员的引用可能并不是一个好主意 - 这样会有效地破坏封装性,允许任何人访问和修改您的私有成员。在您的设计中,这可能是可以接受的或不可接受的。 - Péter Török
@MIkhail:由于Justin自发布问题后一直未上线,我就擅自修复了它。 - sbi
你可以使用 Matrix 库 - Amir Fo
7个回答

28

二维数组不会退化成指向整型指针的指针,而是退化成指向整型数组的指针。也就是说,只有第一维度会退化成指针。该指针不指向整型指针,即不会在递增时按指针大小前进,而是指向包含 5 个整数的数组。

class Myclass {
private:
    int myarray[5][5];
public:
    typedef int (*pointer_to_arrays)[5]; //typedefs can make things more readable with such awkward types

    pointer_to_arrays get_array() {return myarray;}
};

int main()
{
    Myclass o;
    int (*a)[5] = o.get_array();
    //or
    Myclass::pointer_to_arrays b = o.get_array();
}

当每个子数组是单独分配的(即最初有一个指针数组)时,使用指向指针的指针(int**)。

int* p[5];
for (int i = 0; i != 5; ++i) {
    p[i] = new int[5];
}

我们有一个包含五个指针的数组,每个指针都指向一个单独的内存块的第一个项目,总共有6个不同的内存块。

在二维数组中,您可以获得一个连续的内存块:

int arr[5][5]; //a single block of 5 * 5 * sizeof(int) bytes
你应该会发现这些东西的内存布局完全不同,因此它们无法以相同的方式返回和传递。

18

你可以返回两种可能的类型来提供对内部数组的访问。旧的C风格将返回int *[5],因为数组会很容易地衰变成指向第一个元素的指针,该元素的类型是int[5]

int (*foo())[5] {
   static int array[5][5] = {};
   return array;
}

现在,你也可以返回对内部数组的适当引用,最简单的语法是通过typedef:

typedef int (&array5x5)[5][5];
array5x5 foo() {
   static int array[5][5] = {};
   return array;
}

或者没有typedef会更加冗长:

int (&foo())[5][5] {
   static int array[5][5] = {};
   return array;
}

C++ 版本的优点是保持了实际类型,这意味着调用方知道数组的实际大小。


1
虽然要小心使用那些typedef,有时候:delete[] new array5x5()(看起来像new,但实际上是new[])。 - Roger Pate

8
为了返回您的数组成员数组的指针,所需的类型是 int (*)[5],而不是 int **
class Myclass {
private:
    int myarray[5][5];
public:
    int (*get_array())[5];
};

int (*Myclass::get_array())[5] {
    return myarray;
}

2
如何返回一个私有字段中的多维数组?
如果它是被隐藏的,为什么要首先返回它呢?
无论如何,您不能从函数中返回数组,但可以返回指向第一个元素的指针。一个5x5整数数组的第一个元素是什么?当然是一个由5个整数组成的数组。
int (*get_grid())[5]
{
    return grid;
}

或者,您可以通过引用返回整个数组:

int (&get_grid())[5][5]
{
    return grid;
}

欢迎来到C声明符号语法的地狱 ;-)

我可以建议使用std::vector<std::vector<int> >boost::multi_array<int, 2>代替吗?


2
更简单的方法是使用decltype(auto)(自C++14起)。
decltype(auto) get_array() { return (myarray); } // Extra parents to return reference

然后是decltype(自C++11以来)(成员应在方法之前声明)

auto get_array() -> decltype((this->myarray)) { return myarray; }
// or
auto get_array() -> decltype(this->myarray)& { return myarray; }

然后是使用typedef的方式:

using int2D = int[5][5]; // since C++11
// or
typedef int int2D[5][5];

int2D& get_array() { return myarray; }

由于常规语法非常丑陋,因此需要改进:

int (&get_array())[5][5] { return myarray; }

使用 std::array<std::array<int, 5>, 5>(自 C++11 起)将具有更自然的语法。


1

我成功地使用自动类型推导使这个函数在C++0x中工作。然而,如果没有它,我就无法让它工作。原生的C数组在C++中支持得不是很好——它们的语法非常丑陋。你应该使用一个包装类。

template<typename T, int firstdim, int seconddim> class TwoDimensionalArray {
    T data[firstdim][seconddim];
public:
    T*& operator[](int index) {
        return data[index];
    }
    const T*& operator[](int index) const {
        return data[index];
    }
};
class Myclass {
public:
    typedef TwoDimensionalArray<int, 5, 5> arraytype;
private:
    arraytype myarray;
public:
    arraytype& get_array() {
        return myarray;
    }
};

int main(int argc, char **argv) {
    Myclass m;
    Myclass::arraytype& var = m.get_array();
    int& someint = var[0][0];
}

这段代码编译得很好。你可以在 Boost(boost::array)中获得预先编写的包装类,支持整个工具套件。


无法使用GCC编译:第一个return data[index]从rvalue中生成了一个非const引用,它说。 - Cubbi
在这种情况下,应该返回一个裸指针,而不是指向指针的引用。否则,我们至少在可读性方面有所收获。一个小问题:为什么要使用 int 作为实际模板类型?我认为最好使用无符号整数类型来表示不可能为负数的内容。 - Matthieu M.
data[index] 是一个左值,而不是右值,就像 *ptr 是一个左值一样。注意到这个引用可能并不是必要的,我认为它来自代码的先前版本,你可以将其删除。 - Puppy
@Matthieu:这里不必防止负数(编译器会为您处理),int 类型更适合作为默认 int 类型:例如,您想要两个大小之间的差异的情况。 - Roger Pate

-2

将您的int更改为int [] []或尝试使用int [,]?


3
int[][] 也不应该被视为有效。在这种情况下,理解数组和指针的区别非常重要。 - visitor
2
@David:其实new int[5,5]是合法的C++代码,只是它并不会像人们想象的那样执行--这个逗号操作符与new int[5]是等效的 :) - fredoverflow
啊,抱歉,我在想什么?可能是C#吧?哈哈,我的错。 - Harold

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