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++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!