c++使用裸指针与智能指针返回数组详解

Posted bitcarmanlee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++使用裸指针与智能指针返回数组详解相关的知识,希望对你有一定的参考价值。

1.c++无法直接返回数组

首先明确一点:c++代码中无法直接返回数组,这一点比较清晰简单,我们写个方法进行测试

int* f1(int a[3], int b[3]) 
    int c[3];
    c[0] = a[0]*b[0];
    c[1] = a[1]*b[1];
    c[2] = a[2]*b[2];
    return c;


int main(int argc, char const *argv[])

    int a[] = 1, 2, 3;
    int b[] = 4, 5, 6;
    int *c = f1(a, b);
    cout<<c[0]<<" "<<c[1]<<" "<<c[2]<<endl;
    delete[] c;
    return 0;

上面代码运行得到的结果为

point_array.cc:21:12: warning: address of stack memory associated with local variable 'c' returned [-Wreturn-stack-address]
    return c;
           ^
1 warning generated.
4 37105316 1

原因也很容易解释:
我们在f1内定义的数组c,在f1执行完毕以后被系统释放掉,所以在调用f1方法得到的结果肯定就不是计算以后得到的正确结果。

2.使用裸指针表示数组

为了解决上面的问题,我们可以在函数内new一个数组出来,这样new出来的数组被分配在堆空间上,函数执行完毕不会被释放。

int* f2(int a[3], int b[3]) 
    int *c = new int[3];
    c[0] = a[0]*b[0];
    c[1] = a[1]*b[1];
    c[2] = a[2]*b[2];
    return c;


int main(int argc, char const *argv[])

    int a[] = 1, 2, 3;
    int b[] = 4, 5, 6;
    int *c = f2(a, b);
    cout<<c[0]<<" "<<c[1]<<" "<<c[2]<<endl;
    delete[] c;
    return 0;

上面代码运行

4 10 18

因为上面的数组是在函数内new出来,而在调用以外释放,逻辑不是特别清晰,因此可以改为下面的方式

void f3(int a[3], int b[3], int c[3]) 
    c[0] = a[0]*b[0];
    c[1] = a[1]*b[1];
    c[2] = a[2]*b[2]; 


int main(int argc, char const *argv[])

    int a[] = 1, 2, 3;
    int b[] = 4, 5, 6;
    int *c = new int[3];
    f3(a, b ,c);
    cout<<c[0]<<" "<<c[1]<<" "<<c[2]<<endl;
    delete[] c;
    return 0;

因为c++中手动分配内存的一个标准就是谁分配谁释放,因此,我们最好的方式是在函数外new分配内存,并在函数外释放。

3.使用unique_ptr

上面使用指针的方式返回数组,需要手动进行内存管理。如果没有进行内存释放,或者没有在合适的位置释放内存,都会带来内存泄漏的问题。因此从c++11以后,引用了智能指针,可以方便我们进行各种操作而不是担心内存管理问题。

unique_ptr<int[]> f4(int a[3], int b[3]) 
    unique_ptr<int[]> unique(new int[3]);
    unique[0] = a[0] * b[0];
    unique[1] = a[1] * b[1];
    unique[2] = a[2] * b[2];
    return unique;


int main(int argc, char const *argv[])

    int a[] = 1, 2, 3;
    int b[] = 4, 5, 6;
    auto unique = f4(a, b);
    cout<<unique[0]<<" "<<unique[1]<<" "<<unique[2]<<endl;

    return 0;

上面的例子是使用unique_ptr返回一个数组。可以看到还是相当方便,相比于后面介绍的shared_ptr,unique_ptr的使用更为方便,因为他重载了下标运算符,我们可以把他直接像普通数组一样使用。

4.使用shared_ptr

相比unique_ptr,平时使用得更多的是shared_ptr。先看例子。

shared_ptr<int> f5(int a[3], int b[3]) 
    shared_ptr<int> ptr(new int[3], [](int *p) delete[] p;);
    auto p = ptr.get();
    p[0] = a[0] * b[0];
    p[1] = a[1] * b[1];
    p[2] = a[2] * b[2];
    return ptr;


int main(int argc, char const *argv[])

    int a[] = 1, 2, 3;
    int b[] = 4, 5, 6;
    auto ptr = f5(a, b);
    int *p = ptr.get();
    cout<<p[0]<<" "<<p[1]<<" "<<p[2]<<endl;
    return 0;


代码输出结果

4 10 18

在c++11中,shared_ptr严格意义上来说是不支持动态数组的,比如我们如下的声明是错误的,因为shared_ptr中的模板参数不能是int[]

shared_ptr<int[]> ptr2(new int[3], [](int *p) delete[] p;);

下面的方式也是有问题的

shared_ptr<int> ptr2(new int[3]);

因为shared_ptr对非数组类型都使用delete p释放资源,而new int[3]不能直接用delete释放,需要用delete []。

最后的使用方法就是我们上面正确运行方法中的那一句

shared_ptr<int> ptr(new int[3], [](int *p) delete[] p;);

模板参数为int类型,同时用一个lambda函数指定delete方式即可。

同时在c++11中,shared_ptr未重载下标运算符,所以我们赋值取值等操作,需要先试用get()方法得到"原始"指针再进行操作。

5.shared_ptr新版本改进

在后续的c++版本中,shared_ptr针对数组操作有所改进与简化。从第4部分,我们不难看出c++11中用shared_ptr管理动态数组的缺点:
1.数组的形式是int[],而声明初始化的时候我们使用的shared_ptr<int>类型。
2.需要我们手动提供delete方法。
3.未提供下标操作,当需要使用类似下标操作时候比较繁琐。
4.无法使用make_shared方法,无法在异常的时候保证安全。

在c++17上,shared_ptr支持了上面的1,2,3点,可以使用下标进行操作,并且使用int[]做为模板操作。

#include <iostream>
#include <memory>
 
int main()

    std::shared_ptr<int[]> sp(new int[3]());
    for (int i = 0; i < 5; ++i) 
        sp[i] = i+1;
    
 
    for (int i = 0; i < 3; ++i) 
        std::cout << sp[i] << std::endl;
    

可以看出来,上面的操作就跟直接操作数组比较类似了

在c++20中,对第4点做出了支持。

auto up2 = std::make_unique<int[]>(10); // 从c++14开始,分配一个管理有10个int元素的动态数组的unique_ptr
 
// c++2a中你可以这样写,与上一句相似,只不过返回的是shared_ptr
auto sp3 = std::make_shared<int[]>(10);

参考文献

https://www.cnblogs.com/apocelipes/p/10346928.html

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于c++使用裸指针与智能指针返回数组详解的主要内容,如果未能解决你的问题,请参考以下文章

C++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!

C++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!

C++ 智能指针性能

C++指针详解

在 C++ 中使用指针的数组:访问返回的数组时出现分段错误

工厂:如何将临时智能指针传递给函数。 C++