c++0.2-----基于对象的类(包含指针)

本篇文章内容包含:c风格字符串原理,拷贝构造,拷贝赋值,内存分配,内存释放与析构函数,内存空间分类。

代码如下:

 1 class SString
 2 {
 3 public:
 4     SString(const char* p=0);
 5     SString(const SString& str);
 6     SString& operator=(const SString& str);
 7     ~SString(){delete[] p_data;}
 8     char* get_p_data() const {return p_data;}
 9 
10 private:
11     char* p_data;
12 };
13 
14 inline SString::SString(const char* p)
15 {
16     if(p)
17     {
18         p_data=new char[strlen(p)+1];
19         strcpy(p_data,p);
20     }
21     else
22     {
23         p_data=new char[1];
24         p_data[0]='';
25     }
26 }
27 
28 inline SString::SString(const SString& str)
29 {
30     if(str.p_data)
31     {
32         p_data=new char[strlen(str.p_data)+1];
33         strcpy(p_data,str.p_data);
34     }
35     else
36     {
37         p_data=new char[1];
38         p_data[0]='';
39     }
40 }
41 
42 inline SString& SString::operator=(const SString& str)
43 {
44     if(this==&str) //自我赋值检测
45     {
46         return *this;
47     }
48     delete[] p_data;
49     p_data=new char[strlen(str.p_data)+1];
50     strcpy(p_data,str.p_data);
51     return *this;
52 }
53 
54 inline ostream& operator<<(ostream& os,const SString& str)
55 {
56     cout<<str.get_p_data();
57     return os;
58 }

一.c风格字符串:

  char *p=”sdfsadf”;

为何一个字符串常量可以赋值给一个指针?

双引号做了3件事:  
1.申请了空间(在常量区),存放了字符串 
2. 在字符串尾加上了'/0'。    
3.返回地址

  三大函数:拷贝构造,拷贝赋值,析构函数

二.拷贝构造函数:符合构造函数的所有特性,只不过形参类型为本类类型。

拷贝构造函数的调用:

string s1=s2;
string s1(s2); //两者意义相同,因为他们都是在创建的时候初始化。都是调用拷贝构造函数。

  如果类里面有指针,默认的拷贝构造函数是指针的拷贝。如果用指针的赋值,就是浅拷贝,两个指针指向同一块内存地址。试想一下,如果此时一个指针释放了这块空间,那么另一个指针就成了空指针了。由于预期是深拷贝,所以不能使用默认的拷贝构造函数。

  此时应该给被赋值的对象分配一定的内存空间,存放赋值过来的字符串。如果用静态分配的方法,由于不知道每次赋值过来的字符串的长度,因此静态分配不好。此时只能使用动态分配的方法。

三:动态内存分配:

  动态分配内存可完全不止成员变量哦,它会定义cookie,来标志分配了多大内存,还会进行填充,以达到16字节的倍数。在vcnew分配的内存如下图:

其中0x00000041就是cookie41中的4表示分配了64个字节,1表示该内存空间已经分配给用户了。

动态分配的步骤:

例如:

complex *pc=new complex(1,2);

编译器将这个语句转化为三个步骤:

void mem=operator new(sizeof(complex)); //动态分配内存

pc=static_cast<complex*>(men);    //指针类型转化

pc->complex::complex(1,2); //调用构造函数

  使用new关键字在堆里面开辟内存空间。使被赋值的对象的指针成员指向这块内存空间。最后要注意,在进行内存回收时,由于new出来的内存不属于对象成员,它只是成员指向的空间。因此,在析构函数内,也就是成员指针消亡前,一定要delete掉这块动态分配的空间。不然,析构函数会将指向它的指针成员删掉,这块动态分配的内存空间就成了一个孤儿,它会在程序运行期间一直占用内存,成为内存泄漏。

四.释放动态空间和析构函数:

如图:

例如:

delete ps;

编译器转化为两步:

string::~string(ps);
operator delete(ps);

 第一条语句:调用析构函数,释放掉ps所指向的动态分配得到的string对象动态分配的一个数组。由于析构函数为:

~SString(){delete[] p_data;}

即为释放动态分配的数组。

第二条语句:

这一句就是删掉动态分配的complex对象,这个对象里面其实就只有一个动态分配的指针。

注意:

释放动态空间时,若是释放一个string类型的动态数组,注意string类型也会动态分配一个动态数组。

例如:若是创建一个string类型的动态数组:

string *p=new string[3];

在释放时,一定要:

delete[] p;

这样他会唤起三次析构函数。析构掉三个动态分配的char字符串。

否则,调用:

delete p;

只会唤起一次析构函数,只能析构一个动态分配的char字符串。还有两个char字符串成为孤儿。

五.拷贝赋值函数:

核心步骤:由于新旧内存要求的空间长度不一样。因此,要删掉旧空间,创建新空间。

注意:拷贝赋值函数一定要进行自我赋值检测,因为存在自己给自己赋值的可能。如果这样的话,那么下面delete的操作会将动态分配的空间全给释放,这样两边都只剩一个指针,根本就不能进行深拷贝。

六.内存空间分类:

栈:在作用域内,存储变量,然后作用域结束变量消亡。一般变量都在栈里面。

堆:全局内存空间,动态分配。

静态存储区:在作用域结束之后仍然存在,直到整个程序结束。存放静态变量,全局变量。

Static局部变量一定要在能够回到该区域时定义,要不然,回不去的话定义也没意义。

原文地址:https://www.cnblogs.com/yulianggo/p/9262767.html