C++ 开发工程师 第二周笔记 boolan.com

来源:互联网 发布:创可贴的网络意思 编辑:程序博客网 时间:2024/05/29 04:34

  • 第二周
    • string类的设计
      • 构造函数和析构函数
      • 拷贝构造函数
      • 拷贝赋值函数
      • 检测自我赋值
      • output函数
    • stack 栈 heap 堆
      • 生命周期
      • new
      • delete
      • 动态分配得的内存块 VC
      • 动态分配得的array VC
    • 复习string类的实现
    • static
    • 构造函数放在private
    • 类模版 class template
    • 函数模版 function template
    • namespace
    • 其他

第二周

string类的设计

  • 防卫式声明
  • 构造函数
    • 拷贝构造 参数是自己的类型
  • = 拷贝赋值
    • 如果不写,编译器会完全复制过去,对于带指针的类,不好!
    • 参数也是自己类型
  • 数据不应该放到数组,最好是用多少 放多少
  • 析构函数

带指针的类一定要有big three 拷贝构造函数 拷贝赋值函数 析构函数

构造函数和析构函数

c的字符串是一个指针指向字符串头,最后加一个结束符号\0

如果初始化一个空字符串,则长度为1,放进去结束符号。
如果初始化一个非空字符串,则长度为字符串长度strlen+1,不要忘记结束符号

清理,析构函数要将动态分配的内存释放掉。

拷贝构造函数

如果没有,浅拷贝会造成World\0区域没有指针引用,造成内存泄漏,Hello\0被引用两次,修改一个,另一个也同时修改。

上图蓝色的两行等同。

拷贝赋值函数

两个对象都已经存在
1. 清空被赋值的对象
2. 重新分配一个和值一样大的空间
3. 拷贝

检测自我赋值

大气,效率高,否则可能造成数据丢失 甚至崩溃。

output函数

cout可以接受字符串指针。

stack 栈 heap 堆

Stack 是存在与某作用域(scope)的一块内存空间(memory space)。比如,当你调用函数,函数本身会形成一个stack来放置它所接收的参数,以及返回地址。
在函数本体(function body)内声明的任何变量,其所使用的内存块都取自上述stack。

Heap 又称 system heap,是由操作系统提供的一块global内存空间,程序可动态分配(dynamic allocated)从中获得若干区块(blocks)。

生命周期

new

编译器进行如下操作
1. 分配内存 operator new 函数 内部调用malloc
2. 将得到的void指针转型
3. 通过指针调用构造函数 构造函数是成员函数,有this

delete

编译器转化为两个动作
1. 调用析构函数
2. operator delete 释放内存 内部调用free

动态分配得的内存块 VC


图中一个格4字节

灰色部分是调试模式下额外的分配的内存,红色部分是cookie,上下各一个
上下cookie记录分配的长度,比如,64,十六进制为40,最后一个bit代表该块内存是否已分配,故41。因为分配的内存为16的倍数,故可以使用最后一位表示。
VC下 分配的内存一定为16的倍数,pad为填补

其他编译器类似。

动态分配得的array VC

分配数组,vc用一个整数记录个数

array new 要搭配 array delete

delete 根据cookie删除内存,无论是delete还是delete[]都会完整的删除动态分配的内存,此处不会发生内存泄漏。delete[]会调用多次析构函数,用delete可能会导致字符串所指的地址的内存泄漏。
养成好习惯,array new 搭配 array delete

复习string类的实现

  • 防卫式声明
  • class 头
  • 怎么放数据
    • 数组 大小固定不好
    • 指针 动态分配内存 32位 指针4字节
    • 数据 private
  • 准备哪些函数
    • 构造函数
      • 无return type
      • 设计参数 接受一个c字符串
        • 参数不改变 加const
        • 有默认值 可以不给初值
      • 在声明外写函数体 String::String(…)
      • 参数有东西吗
        • 有 计算大小 注意字符串结尾 拷贝数据
        • 没有 空字符串也有结束符
      • inline
    • big three
      • 拷贝构造函数
        • 是构造函数 名字 没有返回类型
        • 参数是自己类型的
          • 传reference
          • 不改变 const
        • 分配空间
        • 拷贝数据
        • inline
      • 拷贝赋值函数
        • 函数名 operator=
        • 参数是自己类型
          • 传引用
          • 不改变参数 参数类型const
        • 返回值
          • 结果是自己类型的
          • by reference?
            • 返回值是不是local object
            • 不是 传reference
        • 旧数据删掉
        • 分配空间
        • 拷贝数据
        • 返回值 便于连续赋值
        • 是否自我赋值
          • 判断来源和目的是不是相同
      • 析构函数
        • 释放所有资源 文件 窗口 内存 等
        • array new 对应 array delete
        • inline
    • 其他辅助函数
      • 取字符函数 get_c_str()
      • 函数是否改变数据 const

static

之前,成员变量有好几份,成员函数只有一份,实际上,成员函数有this指针,来作用于不同对象。

带有static的变量和对象脱离了,只有一份。带有static的函数也是只有一份。

静态函数没有this pointer,因此不能像普通参数一样访问对象的非静态变量,只能存取静态数据。

语法上,需要在类的外面写黄色一行,可以赋值,也可以不赋值,严格讲,这一行叫定义,该行会使变量获得内存。类中的static double m_rate只是声明,这个变量实际上是脱离类的。

调用静态函数有两种方式:
1. 通过对象object调用
2. 通过类名class name 调用

构造函数放在private

单例模式a 只有一个,外部通过getInstance得到自己,该函数返回a,再通过这个a调用其他模式。

不完美,如果根本没有调用这个类,那么这个a一直存在,浪费。

改进

a放到函数里,如果不调用这个函数,a就不存在。

类模版 class template

不指定数据类型,用T代替,template<typename T>

函数模版 function template

这里用template <class T> 和上面的typename T相通。

函数模版不用指出数据类型,编译器会自动进行推倒 argument deduction

重载<运算符,完成大小比较。

标准库中算法使用函数模版。

namespace

为了避免冲突,使用

namespace std{...}

把自己的东西包在std命名空间中,可以分段写。

使用有两种方法
1. 使用命令 directive
using namespace std; 将这个命名空间全部打开
2. 使用声明 declaration
using std::cout;

其他

0 0
原创粉丝点击