首页 > 其他分享 >关于返回值的几种情况

关于返回值的几种情况

时间:2023-11-04 21:35:12浏览次数:44  
标签:返回 return int sth 几种 vector 关于 返回值 拷贝

1.参考链接:关于返回值的几种情况

2.思路

在一个函数的内部,return的时候返回的都是一个拷贝,不管是变量、对象还是指针都是返回拷贝,但是这个拷贝是浅拷贝。

  1. 如果返回一个基本类型的变量,比如:
int a;
a = 5;
return a;

那么就会a的一个拷贝,即5返回,然后a就被销毁了。尽管a被销毁了,但它的副本5还是成功地返回了,所以这样做没有问题。

  1. 但是对于非动态分配(new/malloc)得到的指针,像1那么做就会有问题,比如在某个函数内部:
int a[] = {1, 2};
return a;

那么也会返回指针a的一个拷贝,我们假定a的地址值为0x002345FC,那么这个0x2345FC是能够成功返回的。当return执行完成后,a就要被销毁,也就是0x002345FC所指向的内存被回收了。如果这时候在函数外面,去地址0x002345FC取值,那得到的结果肯定是不对的。这就是为什么不能返回局部指针的原因。返回局部变量的引用的道理和这个类似。

这里也就是不能返回局部变量的应用的原因,这里的应用实际上就是一个隐式指针,但是返回局部指针的做法是危险且错误的!

  1. 对于返回(动态分配得到的)指针的另外一种情况,比如在函数内部:
int a = new int(5);
return a;

这样做是可以的。return a执行完后,a并没有被销毁(必须要用delete才能销毁a),所以这里返回的a是有效的。

  1. 如果不是基本数据类型,比如:
class A
{
public:
               OtherClass * ...
};

如果在某个函数内部有一个A类的局部变量,比如:

A a;
return a;

这时候也会返回a的一个拷贝,如果A没有写深拷贝构造函数,就会调用缺省的拷贝构造函数(浅拷贝),这样做就会失败的;

如果A中提供了深拷贝构造函数,则这样做就是可以的。

实例

实验代码如下:

#include <iostream>
using namespace std;

int some_fun1()
{
    int a = 5;
    return a;                   //OK
}

int* some_fun2()
{
    int a = 5;
    int *b = &a;
    return b;                   // not OK
}

 int* some_fun3()
{
    int *c = new int(5);
    return c;                   // OK, return c执行完后,并没被销毁(必须要用delete才能销毁)
}

 class CSomething
{
public:
    int a;
    int b;

public:
    CSomething(int a, int b)

    {
        this->a = a;
        this->b = b;
    }
};

class CA

{
private:
    CSomething* sth;            // 以指针形式存在的成员变量

public:
    CA(CSomething* sth)
    {
        this->sth = new CSomething(sth->a, sth->b);
    }

  // 如果不实现深拷贝,请注释这个拷贝构造函数
    CA(CA& obj)
    {
         sth = new CSomething((obj.sth)->a, (obj.sth)->b);
    }

    ~CA()
    {
        cout << "In the destructor of class CA..." << endl;
        if (NULL != sth) delete sth;
    }

    void Show()
    {
        cout << "(" << sth->a << ", " << sth->b << ")" << endl;
    }

    void setValue(int a, int b)
    {
        sth->a = a;
        sth->b = b;
    }

    void getSthAddress()
    {
        cout << sth << endl;
    }
};

 CA some_fun4()
{
    CSomething c(1, 2);
    CA a(&c);
    return a;                       // 如果CA没有实现深拷贝,则not OK;如果实现深拷贝,则OK

}

 int main(int argc, char* argv[])
{
    int a = some_fun1();
    cout << a << endl;              // OK
    int *b = some_fun2();
    cout << *b << endl;             // not OK,即便返回结果正确,也不过是运气好而已
    int *c = some_fun3();           // OK, return c执行完后,c并没有被销毁(必须要用delete才能销毁)
    cout << *c << endl;
    delete c;
    CA d = some_fun4();             // 如果CA没有实现深拷贝,则not OK;如果实现深拷贝,则OK
    d.Show();
    return 0;
}

若函数返回值是vector对象时:

vector是STL中定义的一个类,而且实现了拷贝构造函数,在函数返回一个vector类型的对象时,就会调用vector的拷贝构造函数,产生一个临时的副本,在函数外部得到的就是这个临时的副本,而原对象本身此时已经被析构了。返回数组的话,你返回的只是数组的首地址(相当于一个unsigned int值而已),函数返回时,会返回该地址的一个副本,然后数组就被销毁了,即原数组所占用的内存空间,被系统回收,并告诉其他程序,这块内存大家可以用了,在函数外面得到的那个副本(和原数组的首地址一样)本身没有什么问题,但此时它所指向的空间,已经被系统回收了(即前面提到的标记可以被其它程序使用),所以是否还能获取原数组的内容就变得很不可靠了,因为如果被其它程序使用那块内容,数据肯定已经发生了变化,当然如果一直没有程序使用那块内存,你还是可以把数组中的内容读出来的,但这是一种非常不可靠的行为。

vector的底层数据结构是数组,当你用返回对象的方法返回vector时,vector会进行整个数组的拷贝,如果数组较大,那么效率是很低的。

所以,如果你要返回的vector是在函数内部new的,那么可以返回该vector的指针,这样的话你必须注意该vector的释放问题。
另外,由于vector的存储空间位置可能在插入删除的时候变化,所以要小心迭代器的失效等问题。

如果vector 里存的不是基本类型, 而是自定义类型的话,最好重写这个类的拷贝构造函数

标签:返回,return,int,sth,几种,vector,关于,返回值,拷贝
From: https://www.cnblogs.com/trmbh12/p/17809815.html

相关文章

  • 关于双代号网络图里的虚线
    定义:双代号网络图中,虚箭线代表不占用时间和资源的虚拟活动。怎么用:百度了会儿看的一脸懵逼,又看了半天例题,终于看懂这虚线是怎么用的了,记录一下。大概是在A-BC-D的情况下,因为一条线只能对应一个活动,且两个编号之间只能连一条实线,所以就一个直接用实线,另一个用实线接虚箭线辅助作......
  • 关于切片的理解
    slice是一个引用类型,底层引用的是数组。每次扩容后都会指向一个新的底层数组,内存地址也随之改变。扩容规则:在不考虑内存对齐的情况下,1)如果切片的大小小于1024,同时翻倍后的大小可以支持所需的切片大小,则进行翻倍扩容;否则需要按照所需的切片大小进行扩容2)如果切片的大小大于10......
  • 关于电磁指纹识别课题的探索(2)
    #今日总结电磁指纹识别的研究内容涉及多个方面,包括以下几个主要方向:电磁辐射特征提取:这是电磁指纹识别的核心任务之一。研究人员致力于开发有效的算法和方法,从设备的电磁辐射中提取有区分度的特征。这可能涉及到信号处理、频域分析、时域分析、小波变换等技术,以捕捉设备的唯一辐......
  • 关于0-1背包问题
    01背包问题分为二维解法和一维解法,一维解法空间内存占用少二维解法代码如下:n,v=map(int,input().split())goods=[]foriinrange(n):goods.append([int(i)foriininput().split()])#初始化,先全部赋值为0,这样至少体积为0或者不选任何物品的时候是满足要......
  • 关于批量按顺序下载(reduce+promise)
    参考文章promiseresolverejecthttps://www.cnblogs.com/lunlunshiwo/p/8852984.html#4917337reduce按顺序调用https://juejin.cn/post/7030625338065420302?searchId=202311041036275432B88F9F3A984960AA注意点promise结果的使用reduce中的等待结果数组的存储运行截......
  • JavaScript 函数、函数构造、函数调用、参数、函数返回值、变量的作用域、预解析
    一、函数及函数的构造函数是一个可重用的代码块,用来完成某个特定功能。每当需要反复执行一段代码时,可以利用函数来避免重复书写相同代码。函数包含着的代码只能在函数被调用时才会执行,就可以避免页面载入时执行该脚本简单来说就是一个封装,封装的是一个特定的功能,重复使用函......
  • AI问答:关于字符串匹配算法的区别及应用场景,哈希/kmp/字典树/AC自动机
    1. 哈希(Hashing):哈希是一种将字符串转换为唯一标识符的技术,通常用于字符串的快速查找和比较。实现难度相对较低,但需要处理哈希冲突的问题。哈希在处理大量数据的查找和比较问题时非常实用。2. KMP(Knuth-Morris-Pratt):KMP 是一种用于字符串匹配的算法,特别适用于查找子串在主串中的......
  • 『杂项』关于Sublime的配置
      知周所众,SublimeText是一款非常好用的C++IDE文本编辑器。而又众所周知,写代码就是在写文本,所以我们就可以使用一些奇技淫巧配置手段来让SublimeText乖乖成为我们的个性化IDE。基础配置  要想SublimeText成为我们的个性化IDE,首先他得是个C++IDE。想要在SublimeTe......
  • 关于期望相关证明的技巧
    1、线性性E(x+y)=E(x)+E(y)这是最基础的,可以用组合的想法理解,本质就是所谓的“拆开计数”这里最强大的一点在于,不要求变量之间的独立性,以下2个例子都展示了这一点。2、如果式子是求和,则可以考虑在每一个情况上证明式子的正确性,从而说明期望整体的正确性。(要求情况之间,和情......
  • 使用Spring Data JPA,您可以通过定义接口,面来避免Object[]以更优雅的格式返回数据,sql
    使用SpringDataJPA,您可以通过定义接口,面来避免Object[]以更优雅的格式返回数据,sql的返回值和接口的属性名一致。jap会根据sql返回值映射到接口对应属性。cas*_*lin6根据定义,JPA将返回Object[]查询返回带有投影的列表的列表,即来自实体(或多个实体)的一组字段.使用......