当前位置:  编程技术>c/c++/嵌入式
本页文章导读:
    ▪【原创】漫谈C++深浅拷贝      对于一般的对象,如:int a = 10;int b = 20;它们之间的赋值、复制过程是很简单的。但是对于类对象来说,其内部存在各种类型成员变量,在拷贝过程中会出现问题。如下: 1 #include<iostream> 2.........
    ▪为何python现在越来越多的人在用了?      曾几何时,python这门语言我并没有听过,那个时候只知道C,后来学了C++,用了C++ Builder和VS [...] 来自无觅网络的本博客推荐文章: .........
    ▪(转) 内存区划分、内存分配、常量存储区、堆、栈、自由存储区、全局区       一. 在c中分为这几个存储区1.栈 - 由编译器自动分配释放2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收3.全局区(静态区),全局变量和静态变量的存储是放在.........

[1]【原创】漫谈C++深浅拷贝
    来源:    发布时间: 2013-10-14

对于一般的对象,如:

int a = 10;
int b = 20;

它们之间的赋值、复制过程是很简单的。但是对于类对象来说,其内部存在各种类型成员变量,在拷贝过程中会出现问题。如下:

1 #include<iostream>
2 #include<cstring>
3 using namespace std;
4 class String {
5 public:
6 String (const char* psz=NULL) : m_psz(strcpy(new char[strlen(psz?psz:"")+1]),psz?psz:""){
7 cout << "String构造" << endl;
8 }
9 ~String () {
10 if(m_psz) {
11 delete[] m_psz;
12 m_psz = NULL;
13 }
14 cout << "String析构" << endl;
15 }
16 char* c_str(void) {
17 return m_psz;
18 }
19 private:
20 char* m_psz;
21 };
22 int main(void) {
23 String s1("hello");
24 String s2(s1);
25 cout << "s1 " << s1.c_str() << endl;
26 cout << "s2 " << s2.c_str() << endl;
27 s1.c_str()[0] = 'H';
28 cout << "s1 " << s1.c_str() << endl;
29 cout << "s2 " << s2.c_str() << endl;
30 return 0;
31 }

./a.out

编译通过了,运行后出现一堆的错误,为什么?!这就是浅拷贝带来的问题。

事实是,在对象拷贝过程中,如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型成员变量,调用其相应类型的拷贝构造函数。原型如下:

String (const String& that) {}

但凡是编译系统提供的缺省函数,总不是十全十美的。

缺省拷贝构造函数在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标----浅拷贝。

用下图来解释这个问题:

在进行对象复制后,事实上s1,s2里的成员指针m_psz都指向了一块内存空间(即内存空间共享了),在s1析构时,delete了成员指针m_psz所指向的内存空间,而s2析构时同样指向(此时已变成野指针)并且要释放这片已经被s1析构函数释放的内存空间,这就让同样一片内存空间出现了“double free” ,从而出错。而浅拷贝还存在着一个问题,因为一片空间被两个不同的子对象共享了,只要其中的一个子对象改变了其中的值,那另一个对象的值也跟着改变了,正如程序中只改变了s1.c_str()[0] = 'H',然而输出的s1,s2均为
hello,所以这并不是真正意义上的复制。

为了实现深拷贝,往往需要自己定义拷贝构造函数,在源代码里,我们加入自定义的拷贝构造函数如下:

String (const String& that) : m_psz(strcpy((new char[strlen(that.m_psz)+1]),that.m_psz)){
cout << "String拷贝构造" << endl;
}

这样再运行就没有问题了。

在程序中,还有哪些情况会用到拷贝构造函数呢?当函数存在对象型的参数或对象型的返回值时都会用到拷贝构造函数。

而拷贝赋值的情况基本上与拷贝复制是一样的。只是拷贝赋值是属于操作符重载问题。例如在主函数若有:String s3;s3 = s2;这样系统在执行时会调用系统提供的缺省的拷贝赋值函数,原型如下:

void operator = (const String& that) {}

 我们可以自定义拷贝赋值函数如下:

void operator=(const String& that) {
m_psz = strcpy (new char[strlen(that.m_psz)+1],that.m_psz);
}

但是这只是新手级别的写法,考虑的问题太少。我们知道对于普通变量来讲a=b返回的是左值a的引用,所以它可以作为左值继续接收其他值(a=b)=30,这样来讲我们操作符重载后返回的应该是类对象的引用(否则返回值将不能作为左值来进行运算),如下:

String& operator=(const String& that){
m_psz = strcp
    
[2]为何python现在越来越多的人在用了?
    来源:    发布时间: 2013-10-14
曾几何时,python这门语言我并没有听过,那个时候只知道C,后来学了C++,用了C++ Builder和VS [...] 来自无觅网络的本博客推荐文章:
[转]我们需要一种其他人能使用的编程语言
Ubuntu12.04下安装eclipse C/C++开发环境
Lua语言语法讲解的一点准备文章
C++ POD(Plain Old Data)类型
C++\VC开发视频教程推荐 无觅
    
[3](转) 内存区划分、内存分配、常量存储区、堆、栈、自由存储区、全局区
    来源:    发布时间: 2013-10-14
 
一. 在c中分为这几个存储区
1.栈 - 由编译器自动分配释放
2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
4.另外还有一个专门放常量的地方。- 程序结束释放
                                                                                                                                              
在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的"adgfdf"这样的字符串存放在常量区。比如:
int a = 0; //全局初始化区
char *p1; //全局未初始化区
void main()
{
    int b; //栈
    char s[] = "abc"; //栈
    char *p2; //栈
    char *p3 = "123456"; //123456{post.content}在常量区,p3在栈上
    static int c = 0; //全局(静态)初始化区
    p1 = (char *)malloc(10); //分配得来得10字节的区域在堆区
    p2 = (char *)malloc(20); //分配得来得20字节的区域在堆区
    strcpy(p1, "123456");
    //123456{post.content}放在常量区,编译器可能会将它与p3所指向的"123456"优化成一块
}

二.在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
1.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
4.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
5.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)

三. 谈谈堆与栈的关系与区别
具体地说,现代计算机(串行执行机制),都直接在代码底层支持栈的数据结构。这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。这种机制的特点是效率高,支持的数据有限,一般是整数,指针,浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构。因为栈的这种特点,对栈的使用在程序中是非常频繁的。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈,然后跳转至子程序地址的操作,而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。C/C++中的自动变量是直接利用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的原因。 

和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。基本的malloc/realloc/free 函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间,如果没有可以使用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。当程序释放分配的内存空间时,这片内存空间被返回内部堆结构中,可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下若干原因:
1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配);这样的话对于大量的小内存分类来说会造成浪费。
2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。
3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。

堆和栈的对比
从以上知识可知,栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而栈是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。栈是系统数据结构,对于进程/线程是唯一的;堆是函数库
    
最新技术文章:
 




特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3