• C++面向对象高级编程(三)基础篇


    技术在于交流、沟通,转载请注明出处并保持作品的完整性。

    概要

    一.拷贝构造

    二.拷贝赋值

    三.重写操作符

    四.生命周期


    本节主要介绍 Big Three 即析构函数,拷贝构造函数,赋值拷贝函数,前面主要围绕不带指针的class complex本节中主要围绕带指针的String类

    前面我说过如果你创建的类不带有指针,那么多半你可以不用写析构函数,但是如果你创建了一个带指针的类,那么你必须重写Big Three

    创建一个类

    class String
    {
    public:                                 
       String(const char* cstr=0);                     
       String(const String& str);                    
       String& operator=(const String& str);         
       ~String();                                    
       char* get_c_str() const { return m_data; }
    private:
       char* m_data; //由于带有指针 ,所以需要重写析构函数,拷贝构造,赋值拷贝
    };

    一.拷贝构造

    如下操作会调用拷贝构造函数

    String a{"hello"};

    它的实现为

    inline
    String::String(const char* cstr) //拷贝构造
    {
       if (cstr) {
          m_data = new char[strlen(cstr)+1];
          strcpy(m_data, cstr);
       }
       else {   
          m_data = new char[1];
          *m_data = '';
       }
    }

    这个大家都应该理解


    二.拷贝赋值

    下面做赋值拷贝操作

    String a{"hello"};
    String b{"world"};
    s1 = s2;

    若果我们使用编译器自带的赋值拷贝就会发生下面的现象

    这样操作会产生野指针,  因为a和b同时指向 hello  ,没人指向world

    同时如果a和b同时指向同一块内存,如果你删掉a的话  b指向的内存也会被删掉,这可不是我们想要的

     所以我们必须重写赋值拷贝函数,下面是正确的赋值拷贝函数

    inline
    String& String::operator=(const String& str) //拷贝赋值
    {
       if (this == &str) //防止自我赋值  //如果不写这个判断,那么执行带1的时候,就会先杀掉自己,导致错误
          return *this;
    
       delete[] m_data; //1释放自己内存
       m_data = new char[ strlen(str.m_data) + 1 ];//2.创建新的内存
       strcpy(m_data, str.m_data); //3.copy
       return *this;
    }

    赋值拷贝的三个步骤: 1.释放自身内存 2.创建新内存 3.copy

    注意上面的红色部分


     三.重写操作符

    因为string类是你新创建的,所有cout不识别你自己创建的类,所以你要重写一个<<

    #include <iostream>
    using namespace std;
    
    ostream& operator<<(ostream& os, const String& str) //如果写成成员函数调用的时候是这样 c1 << cout  是不是很难接受啊
    {
       os << str.get_c_str();
       return os;
    }

    四.生命周期

    下面我来介绍一个内存管理

    1.Stack(栈),是存在于某作用域 (scope) 的一一块内存空间 (memory space)。例如当你调用函数,函数本身即 會形成一個 stack 用来放置它所接的参数,以及返回地址

    2.Heap, system heap,是指由操作系統提供的 一块 global 內存空間,程序可动态分配 (dynamic allocated) 从某中获得若干区块 (blocks)

    例如

    String s1{"hello"};//stack
    String s2 = new String("world");// 动态分配  heap
    static Complex c2(1,2); //static

    stack 栈 的生命周期在作用域结束之际结束.自动清理

    heap 堆 的生命周期在他被调用delete之际结束

    static 静态对象 生命周期会一直存在带程序结束之际

    global 全局对象 其生命在整个程序结束之后 才结束。你也可以把它视为一种 static object,其作用域 是「整个程序」


    五.内存管理

    先说一下new 和 delete 的调用过程

    1. new:先分配 memory, 再調用 ctor 

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

    编译器会把它翻译成

    void* mem = operator new( sizeof(Complex) ); //分配內存,其内部调用malloc
    pc = static_cast<Complex*>(mem); //转型
    pc->Complex::Complex(1,2); //构造函数 pc->Complex::Complex(1,2);

    2. delete:先調用 dtor, 再釋放 mxemory 

    Complex* pc = new Complex(1,2);
    ...
    delete pc;

    编译器转化为

    Complex::~Complex(pc); // 析构函数
    operator delete(pc);   // 释放內存 其内部调用feee()

    下面是说一下动态分配所得的内存块以VC编译器为例

    debug版 complex类内涵两个double型成员变量(实部虚部)

    每格4字节

    是不是感到有些惊讶,在debug版下 我们仅new complex() 到底给我们带来多少的内存呢

    头部和尾部(红色部分)的00000041是 cookie 是两个4字节内存  cookie负责标记内存 最后一位的1是代表获得内存     如果最后一位是0回收内存  4*2 = 8bit

    灰色部分是VC分期内存是赋予的每块debug内存都会有这块内存 4*8 + 4 = 36bit

    绿色部分是我们的conplex       8bit

    青色部分是补位部分,因为VC下每一块内存必须是16的倍数

    那么我们new一个complex系统应该分配给 (4*2) + 36 + 8 + (4*3 补位) = 64bit

    那么非debug版

     

    4*2  + 8 = 16;

    下面我们来看一下带指针的string类

    debug版

    4+(32+4)+(4*2) = 48

    非debug版

    4+(4*2) + 4 = 16

    可见指针占用的内存小一些

    总结

    1.带有指针的class必须重写Big Three这是一个非常良好的习惯

    2.指针更省内存

    如有不正确的地方请指正

    参照<<侯捷 C++面向对象高级编程>>

  • 相关阅读:
    项目经理如何管理团队
    正则表达式判断中文字符
    售前工作经验总结
    项目管理知识体系九大知识领域
    项目经理如何调动员工的积极性
    项目经理与客户沟通的宜与忌
    关于CodeSmith生成CSLA代码的联想
    接触CSLA框架的感觉
    C#多线程浅接触二、
    WF工作流开发回顾:介绍
  • 原文地址:https://www.cnblogs.com/LearningTheLoad/p/7294674.html
Copyright © 2020-2023  润新知