C++中的函数返回值与拷贝用法

 更新时间:2022年11月25日 09:25:16   作者:白给程序猿  
这篇文章主要介绍了C++中的函数返回值与拷贝用法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

C++函数返回值与拷贝

先来谈谈对C++中函数返回return的理解,自己本来在学Java,但是平时学校的项目是用的C++,所以在平时搬砖时经常会有一些问题,今天就来谈谈前段时间注意到的一个很小的知识点,话不多说,先上列子。

首先我们创建一个简单的Man类,实现它的无参构造函数、有参构造函数和析构函数:

class Man
{
public:
	Man() {
		cout << "构造" << endl;
		data = new int(0); }

	Man(const Man& m)
	{
		cout << "拷贝构造" << endl;
		this->data = m.data;
	}
	
	~Man() 
	{ 
		cout << "析构" << endl;
		delete data; 
	}
	
	int* data;
};

声明一个get函数获取一个Man的对象

Man get(Man& m)
{
	cout << "----" << endl;
	return m;
}

main函数中执行下列代码

 void main()
{
		Man m, n;
		//cout << "before m=" << &m << "n=" << &n << endl;
		*m.data = 5;
		printf("m.data is %d\n", *m.data);
		n = get(m); 

		printf("m.data is %d\n", *m.data);
		printf("n.data is %d\n", *n.data);
	
	    system("pause");
	    }

你可以试着想一想三个printf的输出结果分别是多少

执行结果如下图所示:

在输出结果里我们可以清楚的看到,Man m, n; 创建了m,n两个对象,调用了构造函数,对m对象中的data赋值,然后我们调用get(Man& man) 函数,注意这里函数参数是引用类型,因此传入的对象是m对象本身,这里我们要区别get(Man man) 两种函数参数类型的区别,我稍后再提。get(Man& man) 函数调用完毕后,返回对象m。

按照我们过去的分析会认为对象n等于get函数返回的m对象 (n=m) (注意这里等号=被重载过),m对象中的int* data 成员值直接赋值给了n对象中的data成员,输出时照理说m和n的data值都应该等于5的,但是:

为什么这里输出结果却表明这个data指针指向的空间被销毁了?

为什么get函数执行里会多出了拷贝构造和析构这两个过程呢?

如果我们返回值为Man&会有什么区别变化呢?

这里我们做一个对比,填加一个getR函数,返回值为Man& 引用类型:

Man& getR(Man& m)
{ 
    cout << "----" << endl;
    return m;
}

接下来我们调用getR这个函数看一看输出结果:

void main()
{
        Man m, n;
        *m.data = 5;
        printf("m.data is %d\n", *m.data);
        n = getR(m);
        
        printf("m.data is %d\n", *m.data);
        printf("n.data is %d\n", *n.data);
        
        system("pause");
        }

执行结果如下图所示: 

执行结果如下图所示:

可以看到,当我们返回的是m对象的的引用时,getR 函数执行时没有调用拷贝构造和析构函数

这里我解释一下返回值不是引用的情况时整个函数执行的过程

(个人拙劣的理解)

我们再回到get这个函数:

Man get(Man& m)
{
	cout << "----" << endl;
	return m;
}

首先函数参数传入m这个对象的引用我们毋庸置疑,关键就在return这里。

我们捋一捋函数从开始到结束这个过程,随着Main函数调用get函数,get函数入栈,同时get方法对应的栈帧(储存函数局部变量、返回地址等信息)也入栈,这里的局部变量也就是m对象的引用。

当我们return这个m对象时,会在内存中创建一个临时的Man temp对象,同时这个temp对象调用其拷贝构造函数,也就是Man temp(m) 。

完成temp对象的创建后,get函数出栈,对应的栈区内容被销毁,这时系统会调用m对象的析构函数,注意这里有一个陷阱!!!!

由于m对象是在main方法下的栈区创建的,因此get方法出栈后,系统调用m析构函数并没有真正把m对象在栈区销毁(因为它根本就不是在get方法的栈区上),调用析构函数仅仅是将data指针所指向的内存空间被销毁了(delete data;),这也解释了为什么m.data的值为-572662307。

main方法执行完毕后,m对象才会调用析构函数真正被销毁,当然,这也会带来另一个问题,data指向的内存区被执行了两次delete,运行结束后你也就会发现还会有一个**“析构”**和一个内存问题报错。

回到我们返回的值上:

n = get(m); 

这里实际上可以理解成

Man temp(m);
n=temp;

当然由于get函数的退出调用析构函数时,data指针指向的内存区域数据已经被销毁,自然n和m得到的值是一个错误值了。

总结

对于函数返回值类型为非引用类型(当然引用类型也可以理解为Man& temp=m),都是会在内存中创建一个临时变量,将返回值拷贝到临时变量中,而返回值是作为函数调用栈区中的局部变量,随着函数的返回,栈区的销毁,而被销毁。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • C++数据模型应用在QML委托代理机制中

    C++数据模型应用在QML委托代理机制中

    这篇文章主要介绍了在QML委托代理机制中使用C++数据模型,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • 使用VSCode和VS2017编译调试STM32程序的实现

    使用VSCode和VS2017编译调试STM32程序的实现

    这篇文章主要介绍了使用VSCode和VS2017编译调试STM32程序的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • C语言算法积累分离数位示例

    C语言算法积累分离数位示例

    这篇文章主要为大家介绍了C语言算法积累分离数位的实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • C语言对冒泡排序进行升级介绍

    C语言对冒泡排序进行升级介绍

    大家好,本篇文章主要讲的是C语言对冒泡排序进行升级介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • Qt图形图像开发之曲线图表库QChart编译安装详细方法与使用实例

    Qt图形图像开发之曲线图表库QChart编译安装详细方法与使用实例

    这篇文章主要介绍了Qt图形图像开发之曲线图表库QChart编译安装详细方法与使用实例,需要的朋友可以参考下
    2020-03-03
  • 详解C++中的菱形继承问题

    详解C++中的菱形继承问题

    这篇文章主要介绍了详解C++中的菱形继承问题,在多继承结构中,存在着很多问题,比如从不同基类中继承了同名成员,派生类中也定义了同名成员,这种二义性问题很好解决,加上要访问的基类的类名限制就可以了,需要的朋友可以参考下
    2023-08-08
  • 浅谈C++类型转化(运算符重载函数)和基本运算符重载(自增自减)

    浅谈C++类型转化(运算符重载函数)和基本运算符重载(自增自减)

    下面小编就为大家带来一篇浅谈C++类型转化(运算符重载函数)和基本运算符重载(自增自减)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • 嵌入式项目使用C语言结构体位段特性实现断言宏校验数据范围有效性的方法

    嵌入式项目使用C语言结构体位段特性实现断言宏校验数据范围有效性的方法

    今天小编就为大家分享一篇关于嵌入式项目使用C语言结构体位段特性实现断言宏校验数据范围有效性的方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • QT5中使用QRegularExpression代替QRegExp方法代码

    QT5中使用QRegularExpression代替QRegExp方法代码

    这篇文章主要给大家介绍了关于QT5中使用QRegularExpression代替QRegExp的相关资料,正则表达式(regep)是处理字符串和文本的强大工具,验证regexp可以测试子字符串是否满足某些条件,例如是整数或不包含空格,需要的朋友可以参考下
    2024-04-04
  • Opencv二帧差法检测运动目标与提取轮廓

    Opencv二帧差法检测运动目标与提取轮廓

    这篇文章主要为大家详细介绍了Opencv使用二帧差法检测运动目标与提取轮廓,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03

最新评论