为什么我们不应该使用在类中返回数组或指针的函数呢

Why should not we use a function that returns an array or pointer in a class

本文关键字:数组 指针 函数 返回 不应该 我们 为什么      更新时间:2023-10-16

我的问题是,如果函数返回数组,会出现什么问题?我们可以使用这样的功能吗?

C++不允许函数将数组类型作为prvalue返回;xvalues和lvalues很好,不过:

using T = int[10];
T  & foo();    // OK, returns lvalue
T && foo();    // OK, returns xvalue
T    foo();    // Error, not allowed

函数类型也是如此(尽管在这种情况下,函数调用表达式的结果总是一个左值)。参见[dcl.fct]/10:

函数不应具有类型为数组或函数的返回类型,尽管它们可能具有类型为指针或引用的返回类型。

类似地,数组和函数类型不能是函数参数类型(但对它们的引用可以),但具有此类类型的函数参数的声明含义被调整为"指向{数组元素类型,函数类型}的指针"。

C++和C函数中都不能有数组的返回类型。数组没有复制构造函数或复制赋值运算符。

然而,在C++中,您可以返回对数组的引用。

考虑以下示例

#include <iostream>
#include <numeric>
const size_t N = 10;
int ( & init( int ( &a )[N], int initial ) )[N]
{
    std::iota( a, a + N, initial );
    return a;
}
std::ostream & print( const int ( &a )[N], std::ostream &os = std::cout )
{
    for ( int x : a ) os << x << ' ';
    return os;
}
int main() 
{
    int a[N];
    print( init( a, 0 ) ) << std::endl;
    print( init( a, 10 ) ) << std::endl;
    return 0;
}

输出为

0 1 2 3 4 5 6 7 8 9 
10 11 12 13 14 15 16 17 18 19 

但是,您可能不会返回对函数的本地数组的引用或指针(指向第一个元素)。在这种情况下,程序将具有未定义的行为。

但是您可以使用数组std::array的标准包装器,并从函数返回它。

以下是的示例

#include <iostream>
#include <numeric>
#include <array>
const size_t N = 10;
std::array<int, N> init(int initial = 0 )
{
    std::array<int, N> a;
    std::iota( a.begin(), a.end(), initial );
    return a;
}
std::ostream & print( const std::array<int, N> &a, std::ostream &os = std::cout )
{
    for ( int x : a ) os << x << ' ';
    return os;
}
int main() 
{
    std::array<int, N> a;
    a = init();
    print( a ) << std::endl;
    a = init( 10 );
    print( a ) << std::endl;
    return 0;
}

程序输出与上述相同

0 1 2 3 4 5 6 7 8 9 
10 11 12 13 14 15 16 17 18 19 

正如Mike Seymour所说,函数不能返回数组,只能返回指向其第一个元素的指针。

它本身是完全正确的,但如果误用,可能会导致错误。可能出现的一些问题:

  • 返回一个指向自动数组的指针:当您尝试访问时,一旦函数返回=>UB,if就会被销毁

    int * arr() {
        int arr[10]; // automatic array
        ...
        return arr;  // WRONG ! UB when accessed from caller ...
    }
    
  • 返回一个指向动态分配的数组的指针:很好,但调用方必须在它不再使用时释放它,否则将出现内存泄漏

    int * arr() {
        int *arr = new int[10]; // automatic array
        ...
        return arr;  MUST be freed by caller ...
    }
    for (int i=0; i<5, i++) {
        int *a = arr();
        ... // use a
    }   // no delete[] a in loop => memory leak !
    
  • 返回指向静态数组的指针:很好,但不应在多线程上下文中使用

    int * arr() {
        static int arr[10]; // automatic array
        ...
        return arr;  // as arr is static it will persist after function returns
    }
    

    线程a:

    int *a = arr();
    

    线程b:

    int *a = arr();
    

    现在,两个线程都通过其a指针共享同一个数组,如果数组不是只读的,则通常不会出现

正如我所说,可以将指针返回到非自动数组。它必须谨慎使用,因为它经常会导致问题。

停止使用这些旧东西。开始使用STL。您可以始终将vector or string传递到function parameters中,并始终从函数中获得两者作为return值。

函数不能直接返回数组,因为数组不能简单地复制或移动。

它可以返回一个指向数组开头的指针。如果函数返回后数组继续存在,这是可以的。但是,如果数组是一个局部自动变量,就会出现严重的错误,因为当函数返回时,它会被破坏,留下指针。尝试访问数组的剩余部分会产生未定义的行为。

既然你问了一个例子:

int * bogus() {
    int array[] = {1,2,3,4,5};
    return array;  // Whoops! array is destroyed here
}
int * p = bogus();
assert(p[2] == 3); // BOOM! undefined behaviour

如果数组的大小很小,可以将其封装在类中以返回其副本;标准库为此提供CCD_ 6。

如果您需要动态分配数组(因为它很大,或者只有在运行时才知道大小),那么std::vector就是您的朋友。