【0】C++面向对象--》《堆与拷贝构造函数》详解

本文详细解析了C++程序的内存布局,包括全局数据区、代码区、栈区和堆区的分配机制。重点介绍了new与malloc在堆空间分配上的区别,拷贝构造函数的使用场景,以及深拷贝与浅拷贝的概念。同时,阐述了临时对象的创建和生命周期,无名对象的特性,以及构造函数在类型转换中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

⑴【C++程序的内存格局】:全局变量、静态变量、常量存放在全局数据区,所有类成员函数和非成员函数代码存放在代码区,为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区,余下的空间都被作为堆区。

⑵【分配堆空间的基本方式】:分配堆空间可以用下面两种动态内存分配函数:

①(type*)malloc(size)或

(type *)remalloc(指针p,size)

remalloc是根据原p指向的空间为地址,再重新分配空间的大小。(type*)表示将size大小的空间按type所表示的数据类型分组单位分组

free(p指针):释放返回堆内存空间的函数,返回的是p所指向的空间。

这种方式分配和释放堆空间属于函数执行操作,需要包含头文件mallc.h。

②type * p = new type(或type[n]:表示type类型数组,即连续分配n个type型数据)

delete (p指针)(或 delete [] p):为释放对空间的运算符。

这种方式分配和释放堆空间属于运算符运算语句,是C++语言的一部分,无需包含头文件

上面两种操作如果需要检查是否分配成功,可以检查指向其空间的指针是否等于NULL,如果是这可以用exit(1)来退出程序,exit函数在头文件stdlib.h中。

malloc与new比较:相对于面向对象来说,new具有malloc的所有能力,但new还有malloc所没有的优点:①new在使用上更简洁,可读性更强②new的执行速度比malloc要快的多,因为new是操作符,可以直接像堆分配空间,而malloc是函数调用,需要先按照函数调用的规矩来执行其功能,这样明显造成时间(压栈出栈和返回等)和空间(栈)上的消耗,比如直接通过+号操作符进行两个数据的加操作,远比通过函数调用来执行两个数据的加操作要好得多,因为通过函数来执行在函数你不也得使用+操作符。所以,在大型程序设计中,往往要很多次的分配对空间,此时new明显能提高效率

③new在堆中分配对象空间时为对象空间调用构造函数,而malloc做不到这一点,malloc在堆中创建的堆对象空间数据成员的值只能是随机值,尽管该类中有构造函数也不能调用,既然不能够调用构造函数,那么在上一节中我们知道,如果类的数据成员是引用型数据或常量数据的话,对其初始化将成为不可能,所以,如果该类的数据成员中如果有这两类数据,就不能用malloc来创建堆对象,否则对象的这两类数据将无意义,所以也必然整个类的操作都会出现错误。就算没有这两类数据数据,也不推荐只用malloc,因为还要在使用类对象之前先对对象的数据成员赋合理的值。

而使用new创建堆对象可以调用构造函数例:

class A;

A * p;

p = new A(参数)

如果调用的是默认构造函数则不带括号()。

但同时也要注意:如果p指向的是堆中的对象数组即:p = new A [n];

则只能在创建对象数组的时候调用默认构造函数,即从堆上分配对象数组,只能调用默认构造函数,不能调用其他任何构造函数。

④使用new无需包含任何头文件,而使用malloc需要包含有文件malloc.h。

【综上所述】:在分配对空间时不要用malloc,而应该使用new,C++语言提高new的目的也是为了取代malloc的只用。但是需要记住remalloc的用法,如果是对堆空间进行再分配,即以原堆空间的首地址作为地址在分配大小不同的对空间,例如增加堆空间的数组长度。但需要记住的是如果remalloc再分配的对空间与原空间的数据类型是一样的,则在再分配的空间范围内,原空间不会被释放,即该空间的数据仍保留。例

int p = new int[3];

p[0] = 2; p[1] = 5; p[2] = 6;

p = (int *)remalloc(p,sizeof(int)*5);

p[0] = 2; p[1] = 5; p[2] = 6;依然成立。

p[3],p[4]此时为随机值。

⑶【拷贝构造函数】:

拷贝构造函数的格式:  必须遵守构造函数的要求,还要满足的条件是只有唯一一个类对象的引用形参。

拷贝构造函数被调用的时机:

①赋值操作②参数传递③创建临时对象

非默认拷贝构造函数使用的原则: 需要分配资源时才自定义拷贝构造函数,不然由系统默认提供。

需要注意的是:当一个对象已经分配完空间,且数据成员已有值时,用一个对象复制给该对象是不会再调用拷贝构造函数的,即调用拷贝构造函数的前提是该对象没调用过构造函数。

⑷【深拷贝与浅拷贝】:

⑸【临时对象】:当函数返回一个对象时,要创建一个临时对象以存放返回的对象,分析下面例子:

class  Student

{

public:

Student(char * p = “0”)

{//…

}

protected:

//…

};

Student fn()

{//…

Student  ms(“randy”);

return  ms;

}

int main()

{

Student  s;

s = fn();

//…

}

对于本例是定义一个类和两个函数,其中一个为主函数,所以从主函数分析:

student  s;语句表明创建一个student类对像,所以调用了student类的构造函数,执行该构造函数。

s = fn();语句中,先分析右边fn(); 最终结果是创建了student类对象ms(函数作用域),然后在将ms对象作为返回值代替整个表达式fn()的值,所以相当于fn = ms;

这里fn我们可以把他看作是一个对象变量,所以语句fn = ms 的含义相当于是用对象ms去构造对象变量fn。所以fn不是ms,fn对象的每个成员的值不一定和ms是一样的,fn的成员值是由student类的拷贝构造函数所决定的,所以说s =fn();语句的右值fn()不是函数体fn()内部ms的值,既fn()的值与ms的值有可能不同。ms是函数fn()的局部变量,fn是函数fn()返回的被称为临时对象的变量。所以,所谓的的临时对象就是函数表达式。

因此s = fn();语句的右边程序的运行过程为:调用函数fn(),在创建ms对象时调用student类的构造函数,把ms的值作为函数的返回值时建立临时对象fn,注意此时fn是student类的对象,临时对象的构造调用的是student类的拷贝构造函数。所以在这全过程中共调用了三个不同的函数。

s = fn();语句的整体是用fn临时对象重新构造s对象,要注意此时s的值也是由student类的拷贝构造函数所决定的,但是很明显fn临时对象建立时调用的拷贝构造函数与s对象重新构造时调用的拷贝构造函数是同一个函数,所以s的成员值与fn的成员值基本上是一模一样的,所以此时C++语言的处理机制是把这两次调用拷贝构造函数归并为一次,即此时临时对象fn被s对象覆盖,fn将完全不能体现。在其他情况下临时对象fn构造时是一定会调用拷贝构造函数的,可以用输出语句体现出来。

综上所诉:语句s = fn();程序运行该语句时可以理解为共调用了四次函数:fn()函数,ms调用的构造函数,临时对象fn调用拷贝构造函数,s调用拷贝构造函数。虽然后面两次合并成为一次。

一般规定:创建的临时对象,在整个创建它们的外部表达式范围内有效了,否则无效

即:s = fn();这个外部表达式,当函数fn()返回时产生的临时对象拷贝给s后,临时对象就被当场析构!临时对象fn将不复存在。

⑹【无名对象】:

⑺【构造函数用于类型转换】:略!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值