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

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

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<

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

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<

上面代码运行

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++中手动分配内存的一个标准就是谁分配谁释放,因此,我们最好的方式是在函数外new分配内存,并在函数外释放。

3.使用unique_ptr

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

unique_ptr f4(int a[3], int b[3]) {
    unique_ptr 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_ptr返回一个数组。可以看到还是相当方便,相比于后面介绍的shared_ptr,unique_ptr的使用更为方便,因为他重载了下标运算符,我们可以把他直接像普通数组一样使用。

4.使用shared_ptr

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

shared_ptr f5(int a[3], int b[3]) {
    shared_ptr 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<

代码输出结果

4 10 18

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

shared_ptr ptr2(new int[3], [](int *p) {delete[] p;});

下面的方式也是有问题的

shared_ptr ptr2(new int[3]);

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

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

shared_ptr 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类型。
2.需要我们手动提供delete方法。
3.未提供下标 *** 作,当需要使用类似下标 *** 作时候比较繁琐。
4.无法使用make_shared方法,无法在异常的时候保证安全。

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

#include 
#include 
 
int main()
{
    std::shared_ptr 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(10); // 从c++14开始,分配一个管理有10个int元素的动态数组的unique_ptr
 
// c++2a中你可以这样写,与上一句相似,只不过返回的是shared_ptr
auto sp3 = std::make_shared(10);
参考文献

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1329812.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)