昨晚参加笔试,开错题,有印象中的概念,但目前尚不清楚是怎么回事,什么原理,导致错误的话题。现在总结。

一、C++写内存分配研究

问题考察例如以下,请先不要看答案,看看你是否能做对,呵呵:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaG91cWQyMDEy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

怎么样。晕了没?正确答案及解析例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaG91cWQyMDEy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

解析:char p[] = “...”是一个数组。这个数组是局部变量。char *p = “...”。是一个指针,这个指针指向一个字符串常量。差别在于:数组的话,字符串是存在这个数组里的,由于这个数组属于局部变量(存在栈区)。而当该函数运行完,位于栈区的局部变量就销毁了,就算把数组的地址返回给主函数。主函数也无法訪问到原有字符串了,应该输出乱码。可是,假设是指向字符串常量的指针,这个字符串是放在程序的常量区而不是放在局部变量中,那么把这个常量的地址返回给主函数,主函数也是能够訪问它的。

以下就针对C++中的内存分配做个总结:

一个C/C++编译的程序占用的内存分为下面几个部分:

1. 栈区(stack)

由编译器自己主动分配释放,存放函数地址、函数參数值、局部变量的值等。

2. 堆区(heap)

就是那些由new分配的内存块,他们的分配与释放由程序猿负责。一般一个new就要相应一个delete。假设程序猿没有释放掉,那么在程序结束后。操作系统会自己主动回收。

3. 全局/静态区(static)

全局变量和静态变量的存储是放在一块的,初始化的全局变量和初始化的静态变量在一块区域,为初始化的全局变量和未初始化的静态变量在相邻的还有一块区域。程序结束后由系统释放。

4. 常量存储区(const)

常量字符串就是放在这里的。

5. 程序代码区

存放函数体的二进制代码。

当中。堆栈的主要差别例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaG91cWQyMDEy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" height="506" width="888">

爽歪歪了吧。以下就来看第二个问题吧。。

二、考察复制构造函数

问题描写叙述例如以下。问该程序的三种情况:A 程序编译错误。B 程序编译成功。执行时出现错误。C 程序正常执行,输出10。

答:A

解析:改题考察了复制构造函数的相关知识,在《C++ Primer》中解说了。复制构造函数的定义形式为:类名(const 类名 &变量名),即參数为类类型的引用。可是为什么非要必须这样定义呢?当然。const的意思很清楚,由于一般复制操作不希望改动实參中的值,因此我们用const来限定一下,当然这个const也能够去掉。即:A(A &other)。我们来分析一下引用的必要性:

1. 防止死循环的递归调用

复制构造函数会在下面情况下调用:

1) 一个对象以值传递的方式传入函数体;

2) 一个对象以值传递的方式从函数体返回。

3) 一个对象须要通过另外一个对象进行初始化。

因此,假设将复制构造函数定义为:A(A other),那么当我们使用A b = a;时,实际上相当于将a作为实參传递给other,而这样的情况下相当于1) 一个对象以值传递的方式传入函数体,又会触发复制构造函数的调用,而在调用时又会触发下一轮的复制构造函数的调用,关键的是採用这样的方式调用过程无法结束,会陷入死循环。因此,复制构造函数的形參必须是引用类型。

2. 高效率的引用

引用比較高效,传递引用能够避免复制(这也能够用来解释上一个原因)。假设一个数据对象相当的大。进行复制会浪费非常多时间,同一时候另一些类型是不支持复制的,像IO类就是不能够复制的。传递引用就能够避免这些问题了。

通过以上的讨论我们还能够引出浅拷贝与深拷贝的问题。

对于普通类型的对象来说,它们之间的拷贝非常easy,比如:int a = 10 ; int b = a ;可是对于类类型的对象,在某些情况下就要考虑特殊的问题。

浅拷贝:浅拷贝就是对象成员之间的简单赋值。比如,当你定义了一个类而没有提供它的复制构造函数,当时用该类的一个对象去给还有一个对象赋值时所运行的过程就是浅拷贝。

假设对象中没有其他的资源(如:堆、文件、系统资源等)。则深拷贝和浅拷贝没有什么差别,但当对象中有这些资源时,就必需要自己定义复制构造函数,来对这些对象进行合理的控制。

深拷贝:深拷贝指的是当拷贝对象中有其它资源(如堆、文件、系统等)的引用时(引用能够是指针或引用),对象得另开辟一块新的资源。而不再对拷贝对象中有对其它资源的引用的指针或引用进行单纯的赋值。如:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaG91cWQyMDEy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

当我们通过例如以下使用它时:

int main()

{

B b = 10;

B c = b;

return 0;

}

假设未定义如上红色标注的复制拷贝函数,则是浅拷贝。它只进行简单的成员赋值。运行B c = b; 后c和b中的data都指向同一块内存区。当b运行析构时,它的data所指的内存区就被释放,而此时c的指针仍然指向那块区域。利用data指针再訪问或c析构时都会发生内存泄露或程序崩溃。加上红色标注部分即为深拷贝。在进行B c = b;时,c会又一次分配一份空间,并将值拷贝过来。此时B析构完之后。C仍然能够訪问。由于此时b和c指向的是两块不同的内存地址。

三、总结

通过上面的问题,发现自己对一些关键点理解的还是不够透彻。都知道它要考察啥,但是大脑里面知识结构太混乱,终于还是没有得出正确答案。《C++ Primer》然后,巩固一下吧。油!

!!

版权声明:本文博主原创文章。博客,未经同意不得转载。

C++内存分配和拷贝构造函数写研究的更多相关文章

  1. String类型_static成员_动态内存分配_拷贝构造函数_const关键字_友元函数与友元类

    1:String类型 #include <iostream> using namespace std; int main() { //初始化方法 string s1 = "hel ...

  2. 漫步Facebook开源C++库Folly之string类设计(散列、字符串、向量、内存分配、位处理等,小部分是对现有标准库和Boost库功能上的补充,大部分都是基于性能的需求而“重新制造轮子”)

    就在近日,Facebook宣布开源了内部使用的C++底层库,总称folly,包括散列.字符串.向量.内存分配.位处理等,以满足大规模高性能的需求. 这里是folly的github地址:https:// ...

  3. [C++参考]拷贝构造函数的参数必须是引用类型

    在C++中, 构造函数,拷贝构造函数,析构函数和赋值函数(赋值运算符重载)是最基本不过的需要掌握的知识.在effective C++中说过这么一点:拷贝构造函数的参数必须是引用类型的.但是为什么呢? ...

  4. QVector的内存分配策略

    我们都知道 STL std::vector 作为动态数组在所分配的内存被填满时.假设继续加入数据,std::vector 会另外申请一个大小当前容量两倍的区域(假设 n > size 则申请 n ...

  5. java基础(5)---内存分配

    一.内存分配 如: 先写下面的源代码:  如果继续写:

  6. 标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)

    标准C++类std::string的内存共享,值得体会: 详见大牛:https://www.douban.com/group/topic/19621165/ 顾名思义,内存共享,就是两个乃至更多的对象 ...

  7. S5PV210的内存分配研究分析

    S5PV210内存一般会使用SDRAM和DDR2 (DDR SDRAM),SDRAM的uboot启动网络已经有很多资料的,对于DDR2还有有很多疑惑,如果有错误的地方,请大家一定指出,醍醐灌顶,不胜感 ...

  8. 内存的分配VS回收&构造函数VS析构函数

    之前有一个问题一直困扰着我,就是一个变量出了作用域,我以为这个变量的内存就被回收了,其实不是这样的,昨天问了一个高手,才豁然开朗,自己在看相关代码的反汇编代码,才知道原来真是这样就.这个问题,我想简单 ...

  9. Com组件的内存分配和释放,CredentialProvider SHStrDup 字符串拷贝问题

    一.简单介绍 熟悉CredentialProvider的同学应该知道,他为一个Com组件,于是,在这里的内存分配(字符串拷贝)的一系列操作就要依照con的标准来. 二.Com组件的内存分配和释放 CO ...

随机推荐

  1. 《Linux Device Drivers》 第十七章 网络驱动程序——note

    基本介绍 第三类是标准的网络接口Linux设备,本章介绍的内核,其余的交互网络接口描述 网络接口,必须使用特定的内核数据结构本身注册,与外部分组交换数据线打电话时准备 经常使用的文件上的网络接口操作是 ...

  2. .NET Core 1.0、ASP.NET Core 1.0和EF Core 1.0简介

    .NET Core 1.0.ASP.NET Core 1.0和EF Core 1.0简介 英文原文:Reintroducing .NET Core 1.0, ASP.NET Core 1.0, and ...

  3. SpringMVC+Spring+Hibernate的小样例

    Strusts2+Spring+Hibernate尽管是主流的WEB开发框架,可是SpringMVC有越来越多的人使用了.确实也很好用.用得爽! 这里实现了一个SpringMVC+Spring+Hib ...

  4. Zygote过程【3】——SystemServer诞生

    欢迎转载.转载请注明:http://blog.csdn.net/zhgxhuaa 在ZygoteInit的main()方法中做了几件大事.当中一件便是启动Systemserver进程.代码例如以下: ...

  5. javascript有用小技巧—实现分栏显示

    记得给师哥师姐測试考试系统的时候,看到他们的考试页面能够实现隐藏左边的考生信息部分,当时认为好高大上.好人性化. 如今学了javascript,我也能实现这个功能了,以下来显摆一下. 1.页面设计: ...

  6. php学习笔记--高级教程--读取文件、创建文件、写入文件

    打开文件:fopen:fopen(filename,mode);//fopen("test.txt","r"): 打开模式:r  仅仅读方式打开,将文件指针指向 ...

  7. 在线maven 仓库

    findmaven.net是一个查找Jar和查找Maven的Maven仓库搜索引擎.它能够依据Java开发人员提供的Class名或者Jar名找到包括它的Jar,同一时候提供Jar的Maven仓库链接, ...

  8. DataGridView绑定数据源

    给DataGridView绑定数据源比較简单,方法主要有两种: 1.直接在控件属性中绑定数据源,这样的方法最简单,但它是直接连接数据库的,这样就和传DataTable的后果差点儿相同了,所以还是尽量避 ...

  9. Google API快速生成QR二维码

    Google API快速生成QR二维码 现在来说生成二维码最简单的方法是使用Google Chart API来实现,再次膜拜Google大神- Google Chart API是一套可以让你在线生成报 ...

  10. Socket编程实践(4) --更复杂的过程server

    1.Socket地址复用 int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); in ...