C++内存分配与复制构造函数笔试考察

昨天晚上去参加笔试了,有两道题做错了,都是印象里面有概念,但是没有弄清楚它到底是怎么回事,原理是什么,导致题目打错,现总结一下。

一、C++内存分配笔试考察

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

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

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

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

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

1. 栈区(stack)

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

2. 堆区(heap)

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

3. 全局/静态区(static)

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

4. 常量存储区(const)

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

5. 程序代码区

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

其中,堆栈的主要区别如下:

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

二、考察复制构造函数

问题描述如下,问该程序的三种情况: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类就是不可以复制的,传递引用就可以避免这些问题了。

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

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

浅拷贝:浅拷贝就是对象成员之间的简单赋值。例如,当你定义了一个类而没有提供它的复制构造函数,当时用该类的一个对象去给另一个对象赋值时所执行的过程就是浅拷贝。如果对象中没有其它的资源(如:堆、文件、系统资源等),则深拷贝和浅拷贝没有什么区别,但当对象中有这些资源时,就必须要自定义复制构造函数,来对这些对象进行合理的控制。

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

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

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》再巩固一下吧。加油!!!

时间: 2024-10-10 22:54:21

C++内存分配与复制构造函数笔试考察的相关文章

C++内存分配和拷贝构造函数写研究

昨晚参加笔试,开错题,有印象中的概念,但目前尚不清楚是怎么回事,什么原理,导致错误的话题.现在总结. 一.C++写内存分配研究 问题考察例如以下,请先不要看答案,看看你是否能做对,呵呵: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaG91cWQyMDEy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" > 怎么样.晕了没?正确答案及解析例如

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

1:String类型 1 #include <iostream> 2 using namespace std; 3 int main() 4 { 5 //初始化方法 6 string s1 = "hello";//默认构造方法 7 string s2(s1);//将s2初始化为s1的一个副本 8 string s3("value");//将s3初始化为字符串的副本 9 string s4(10,'x');//将字符串初始化为字符x的10个副本 10 co

C++ Primer 学习笔记_98_特殊工具与技术 --优化内存分配

特殊工具与技术 --优化内存分配 引言: C++的内存分配是一种类型化操作:new为特定类型分配内存,并在新分配的内存中构造该类型的一个对象.new表达式自动运行合适的构造函数来初始化每个动态分配的类类型对象. new基于每个对象分配内存的事实可能会对某些类强加不可接受的运行时开销,这样的类可能需要使用用户级的类类型对象分配能够更快一些.这样的类使用的通用策略是,预先分配用于创建新对象的内存,需要时在预先分配的内存中构造每个新对象. 另外一些类希望按最小尺寸为自己的数据成员分配需要的内存.例如,

《C++ Primer Plus》12.6 复习各种(类和动态内存分配的)技术 笔记

12.6.1 重载<<运算符要重新定义<<运算符,以便将它和cout一起用来显示对象的内容,请定义下面的友元运算符函数:ostream & operator<<(ostream & os, const c_name & obj){    os << ...;  // display object contents    return os;}其中,c_name是类名.如果该类提供了能够返回所需内容的公有方法,则可在运算符函数中使用这些

C++ Primer 学习笔记_98_非一般工具与技术 -优化内存分配

特殊工具与技术 --优化内存分配 引言: C++的内存分配是一种类型化操作:new为特定类型分配内存,并在新分配的内存中构造该类型的一个对象.new表达式自动运行合适的构造函数来初始化每个动态分配的类类型对象. new基于每个对象分配内存的事实可能会对某些类强加不可接受的运行时开销,这样的类可能需要使用用户级的类类型对象分配能够更快一些.这样的类使用的通用策略是,预先分配用于创建新对象的内存,需要时在预先分配的内存中构造每个新对象. 另外一些类希望按最小尺寸为自己的数据成员分配需要的内存.例如,

C++ Primer 学习笔记_98_特殊的工具和技术 --优化内存分配

特殊的工具和技术 --优化内存分配 引言: C++的内存分配是一种类型化操作:new为特定类型分配内存,并在新分配的内存中构造该类型的一个对象.new表达式自己主动执行合适的构造函数来初始化每一个动态分配的类类型对象. new基于每一个对象分配内存的事实可能会对某些类强加不可接受的执行时开销,这种类可能须要使用用户级的类类型对象分配能够更快一些. 这种类使用的通用策略是,预先分配用于创建新对象的内存,须要时在预先分配的内存中构造每一个新对象. 另外一些类希望按最小尺寸为自己的数据成员分配须要的内

c++的复制构造函数,(郑莉 c++语言程序设计)

1. https://www.cnblogs.com/BlueTzar/articles/1223313.html 以上博客讲了深拷贝和浅拷贝的区别,即: 在某些状况下,类内成员变量需要动态开辟堆内存,这时实行拷贝 B=A; A的成员指针变量有指向内存,这时复制构造函数会将B的也指向同一块内存,A销毁(比如析构)时,B里面的成员变量就成了野指针.所以默认的复制构造函数是不行的.这时就需要深拷贝. 2.何种情况下自动调用复制构造函数 1)对象以值的形式传递到函数时 2)对象以值的形式作为函数的返回

继承和动态内存分配——需要为继承类定义 显式析构函数、复制构造函数和赋值运算符

当派生类使用了new时,必须为派生了定义显式析构函数.复制构造函数和赋值运算符.(这里假设hasDMA类继承自baseDMA类)显式析构函数: baseDMA::~baseDMA() // takes care of baseDMA stuff { delete [] label; } hasDMA::~hasDMA() { delete [] style; } 复制构造函数: baseDMA::baseDMA(const baseDMA & rs) { label = new char[std

C++复制构造函数和赋值符的区别

From  http://blog.csdn.net/randyjiawenjie/article/details/6666937 感谢原作者分享. class CTest{public: CTest(); //构造函数 CTest(const CTest &); //复制构造函数 CTest & operator = (const CTest &); //赋值符}; CTest::CTest(){ cout<<"Constructor of CTest&qu