C++构造函数、拷贝构造函数、赋值函数、析构函数在函数调用中的使用过程
来源:互联网 发布:求购信息搜索软件 编辑:程序博客网 时间:2024/06/05 20:10
一、问题
先上代码
/*C++构造\析构\赋值\函数调用by cjc2014/3/27*/#include<iostream>using namespace std;class B{public:B():data(0){cout<<"default constructor"<<endl;}~B(){cout<<"destructed by parameter "<<data<<endl;}B(int i):data(i){cout<<"constructed by parameter "<<data<<endl;}B(B &b){data=b.data;cout<<"copyed by parameter "<<data<<endl;}B& operator =(const B &b){this->data=b.data;cout<<"= by parameter "<<data<<endl;return *this;}private:int data;};B Play(B b){return b;}void main(void){Play(1);B t1=Play(2);B t2;t2=Play(3);return;}
以上是程序的执行结果,如果你能完全理解其中的原理,下面的文章就是渣渣啦,可以忽略……下面对程序的执行过程进行详细地介绍,不妥之处,欢迎批评指正,不胜感激,进入正题。
二、函数调用的过程
这里先不解释上面的结果,首先补充一下C语言函数调用的东西。
明确几个问题:
1、函数的形式参数是局部变量,它隶属于所在的函数。
为什么不是其它类型变量呢?比如说全局变量……额,明显不可能是这样的,之所以是局部变量,可以通过学习C语言时美女老师讲解的“值传递和指针传递”的问题来理解。既然是局部变量那么在函数调用时,形式参数是怎样传递的呢?学习过C语言的都知道,通过实参拷贝构造了形式参数,即将一个程序空间的局部变量传递给另外的一个程序空间。局部变量的生命期是所在函数的生命期。
2、函数的返回值是复制产生的。
同样是两个程序空间传值问题。一般来说, 局部变量的作用域只在所属函数内部,在函数返回后,局部变量的内存已经释放了。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错,因为在返回的时候传递的是复制后的局部变量,即一个副本。但是如果返回的是局部变量的地址(指针)的话,程序运行后一般会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放了,即野指针,这样指针指向的内容就是不可预料的东东,调用就会出错。准确的来说,函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针是可以的)。
下面以函数返回局部变量的指针举几个典型的例子来说明:
(1)指向只读数据段的指针可返回
#include <stdio.h>#include <stdlib.h>char* func(void){char* p="cjc is a good boy";return p;}int main(void){char* str;str=func();printf("%s\n",str);system("pause");return 0;}
这里输出是没有问题的,原因是,指针p指向的是一个常量字符串,子程序func结束之后,指向.rodata(只读数据段)的指针仍然有效,因为只读数据段的内容(程序编译时已分配)并没有释放。
(2)指向栈的指针不能返回(自动变量指针不能返回)
#include <stdio.h>#include <stdlib.h>char* func(void){char p[]="cjc is a good boy";return p;}int main(void){char* str;str=func();printf("%s\n",str);system("pause");return 0;}
输出的结果出现了问题,因为"cjc is a good boy"是一个局部自动变量,存放在栈中,子程序结束会被释放。
(3)静态变量指针可以返回
#include <stdio.h>#include <stdlib.h>char* func(void){static char p[]="cjc is a good boy";return p;}int main(void){char* str;str=func();printf("%s\n",str);system("pause");return 0;}
突然又没有问题啦,不同点在于这里使用了关键字static,静态变量使用的空间在程序编译时已经确定,子程序结束不会被释放。
(4)堆内存要手动释放
#include <stdio.h>#include <string.h>#include <stdlib.h>char* func(void){char p[]="cjc is a good boy";char* str;str=(char*)malloc(sizeof(char)*20);strcpy(str,p);return str;}int main(void){char* str;str=func();printf("%s\n",str);system("pause");return 0;}
额……“cjc still a good boy”,原因是malloc分配的是堆内存,对于堆内存要手动释放。
三、原问题分析
1、调用Play(1)的过程
首先传递参数1给Play函数,由于Play函数的形参是B类型,因此调用B相应的构造函数生成对象b,当执行return b;时,调用copy构造函数生成传回的对象(复制品),随即函数结束,局部变量b被析构,由于传回的复制对象,在主程序中没有承载的载体,故马上被析构。
2、B t1=Play(2);的执行过程
与1中不同的是,此时传回的对象是由对象t1来承载的,故传回的复制品的生命期与t1相同,在主程序结束时析构。这里"="并非赋值,因为t1在函数Play(2)返回时并不存在,t1在这里起到一个承接返回值的作用,与此同时构建自己。
3、t2=Play(3);的执行过程
函数的分析与上面完全相同,这里"="是赋值操作,调用赋值函数,原因是t2是原来已经存在的对象。最后的两个"destructed by parameter 3"一个是Play(3)返回的复制品的析构,一个是t2的析构。
四、C语言中的各种变量是如何存储的
参见1
参见2
本文第二部分参考了参考1
- C++构造函数、拷贝构造函数、赋值函数、析构函数在函数调用中的使用过程
- 类的构造函数、拷贝构造函数、赋值函数、析构函数,分别在何时调用。
- 构造函数,拷贝构造函数,赋值函数,析构函数
- 构造函数,拷贝构造函数,赋值函数,析构函数
- 构造函数、析构函数、拷贝构造函数、赋值函数
- 构造函数、拷贝构造函数、赋值函数、析构函数的调用顺序
- 构造函数 拷贝函数 赋值构造函数
- 构造函数 拷贝函数 赋值构造函数
- 构造函数、拷贝构造函数、赋值函数
- 【C++】拷贝构造函数和赋值函数
- 赋值函数、拷贝构造函数
- 拷贝构造函数赋值函数
- 拷贝构造函数,赋值函数
- 拷贝构造和赋值函数(pushback调用拷贝构造函数)
- 深入C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程总结
- C++中的构造函数,拷贝构造函数,析构函数和赋值运算符重载
- c/c++ 构造函数、拷贝构造函数、析构函数和赋值运算符重载
- C++中的构造函数+拷贝构造函数+析构函数
- 在ubuntu10.04上安装svn1.8.5
- POJ 1436 Horizontally VisibleSegments(线段树:区间覆盖染色)
- 虚拟机+可信+云计算
- Linuxx下安装软件方法汇总
- jquery 设置输入框背景样式透明,按钮背景样式保存不变
- C++构造函数、拷贝构造函数、赋值函数、析构函数在函数调用中的使用过程
- 【Bellman_Ford】poj 1860 Currency Exchange
- 关于指针的一些用法
- Airport Express
- ACM计算几何题目推荐
- python 多线程+gzip压缩 爬虫
- spring学习笔记4——SpEL表达式
- 使用throw抛出异常
- java xml解析 学习笔记(4)——DOM4J