• 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局部变量一定要在能够回到该区域时定义,要不然,回不去的话定义也没意义。

  • 相关阅读:
    MySQL-存储过程
    MySQL-触发器
    MySQL自学笔记
    arrayList和LinkedList区别
    RecyclerView和ListView比较
    【二叉树遍历】必知方式
    进程与线程的区别
    【单例模式】java实现
    【斐波那契数列】java探究
    replugin插件化,插件转场动画失效的问题解决
  • 原文地址:https://www.cnblogs.com/yulianggo/p/9262767.html
Copyright © 2020-2023  润新知