zoukankan      html  css  js  c++  java
  • GeekBand


    1. C++面向对象高级开发

    1.1 Object Based

    • 基本功
    • with pointer
    • without pointer
    • 面对的是单一calss设计

    1.2 Object Oriented

    • 有关联的calss
    • 多个类的设计

    1.3 History

    • C 1972
    • C++ 1983
    • new C -> C with Class -> C++
    • 大部分版本C++ 98
    • C++ 11 (2.0) 新的关键字
    • C++ 14

    1.4 推荐书

    1.4.1 语言类

    • 左侧:编译器作者
    • 右侧:C++之父

    1.4.2 如何书写

    1.4.3 标准库

    • 牛人 Scott Meyers, Stan Lippman
    • C++ Premier 中规中矩
    • Effective C++/More Effective C++/Inside C++ Object Model

    2. 头文件与类声明

    • C语言


    - 语言没有关键字,数据必须是全局的

    • C++语言

      • 数据和函数包在一起
      • 数据之间不会混杂
      • class:就是增强的struct结构,提供了很多关键字
    • C++,关于数据和函数

      • 复数的class设计
        • 涉及到实部虚部的设计
        • 各种运算关系
      • 字符串的struct设计
        • 内容通过指针来查找
        • 四个字符串,但大小只包含一个指针

    2.2 C代码的基本形式


    - 不同平台的文件的命名不同.h, .hpp, .cpp
    - 标准库include <iostream.h>
    - 非标准库include "complex.h"

    • 数据的输出
      • C++使用cout方式输出数据来调试
      • C的printf必须明确数据的类型

    2.2.1 Header中的方式声明

    • 防卫式声明
      • 用头两行来满足,多个文件同时调用时,处理.h文件的顺序。
    • 各类声明
    • 复数的类的基本组成
    • class template介绍

      • T根据不同的定义<double><int>来重新定义
    • inline 内联函数

      • 函数在本体里面定义,就成为inline
      • 调用效率高
      • 如果函数太复杂,则没法写成inline
    • 访问级别(数据在private是好习惯)

      • private不可直接调用,一般是数据部分
      • public可以被外界调用
    • 构造函数(不带指针)

      • 函数名称与类相同,没有返回类型
      • 初始函数列,用: re(r), im(i)的写法,这种写法表示初始化,不同于赋值。
      • 不带指针,不需要写析构函数
      • complex c1(2,1),调用构造函数,直接复制r=2, i=1
      • complex c2,调用构造函数,默认参数
        • complex * p = new complex(4),调用构造函数,得到指针。疑问???
    • 构造函数的overloading(重载)

      • real取得实部或设定实部,编译时有所不同(如右下角所示)。注意不添加const
      • complex () : re(0), im(0) {}构造函数重构是不行的。
    • 构造函数放在private位置

      • 构造函数放在privage后,则不能直接调用
    • 构造函数的设计模式

      • 外界的getInstance(名称随意)来取得内部函数
      • 有这类需求
    • const member functions(常量成员函数),设计更好的函数

      • 函数的后面添加const,在real()imag()之后,只是将数据取出来
      • 如果没有添加const,但使用者调用const complex c1(2,1),说明函数不改变数据,则与构造函数冲突。
    • 参数选择pass by value or pass by reference (to const)(传reference是好习惯)

      • by value,将所有的数据的传递过去,避免stack数据过大
      • by reference,C语言是通过指针,C++是通过reference(引用的底部就是指针,速度快)
      • const complex&传递到const,传递过去但不修改数值
      • ostream& os传递到非const,传递过去,修改数值
      • c2 += c1传递的是引用
      • ostream& os, const complex& x,第一步分是非const,第二部分是const
    • 返回值传递returen by value or by reference (to const)

      • complex&返回值传递的是reference
    • friend友元

      • friend可以来拿class里面的数据
      • complex& __doapl (..) 是frined
      • 因为是友元,ths直接读取reim
    • 同一个class的各个object互为友元

      • int func(..)目的是计算实部虚部之和,直接读取reim
      • c2.func(c1),用c2的函数来处理复数c1
    • class body外的各种定义

      • complex* ths的数值会被改动
      • const complex& r的数值不会被改动
      • 函数的操作结果:创建一个地方存放
      • 函数的操作结果:放在其中一个参数
    • operator overloading 操作符重载之一,成员函数

      • 本质上就是一种函数,是C++的特点
      • 所有成员函数的隐藏成员this,调用者
      • 这里c2就是this,编译器会把c2地址传递过去
      • c1就是r
      • 实际写的时候,函数成员列表不必写this ,但可以在里面直接使用
      • _doapl,do assignment plus,标准库的代码
    • return by reference

      • return *ths,返回的是object,但声明指的是complex&,是引用。二者可以搭配使用
      • *指返回的value,传递者无需知道接收端用reference形式来接收
      • c1是value,用complex& r来接收
      • c3 += c2 += c1的使用,则重载函数的返回值必须明确complex&,而不能使用void
    • class body之外的重定义

      • 用引用来传递const complex& x
    • 非成员函数的操作符重载

      • +也许找成员函数,也许找非成员函数,但不能重复
      • 这里的三个版本对应不同的情况,复数+复数,复数+实数,实数+复数
    • temp object临时对象

      • 蓝色部分,不是return by reference,而是return by value
      • 运算的返回值,必定是local
      • 在本体运算的结果,如果用reference传出去,如果本体死掉,则数据也就没有了
      • 因为这里只是+运算,还没有执行=,所以运算的结果在执行+运算时,其实不知道要传递给谁
      • +=运算,在执行+=时,已经知道运算结果是要赋值给左侧的变量,因此可以传reference
      • 这里使用typename()的语法,创建临时对象
      • 临时存储临时计算的结果,因为结果既不放在左侧也不放在右侧
      • 右侧黄色部分也是临时对象,complex()得到(0,0),complex(4,5)得到(4,5),这两个执行完,内容就消失了。

      • +-的使用需要明确是加减还是正负
      • 通过参数的个数来确定
    • 特殊的操作符,必须写成全局函数

      • cout是对象,其class name是ostream,传递引用ostream& os,但不可以用const,因为添加之后,os不可以改变,这与实际不符。因为每次执行<<os都在改变,所以不能添加const

        函数设计:考虑是否用引用,是否用const

      • 不能改成void,否则则不能使用连续<<

    3. Complex的实现过程

    • Step 1: 防卫式定义
    1. #ifndef __MYSTRING__
    2. #define __MYSTRING__
    3. ...
    4. #endif
    • Step 2: 考虑数据类型,在privatre书写
    • Step 3: 书写哪些函数

      • 构造函数是否需要默认值和处置列
      • 其他函数是成员函数还是非成员函数
      • 每个函数的参数传递是by reference/value
      • 取得私有数据的函数
      • 函数是否需要添加const
        1. double real() const { return re;}
        2. double image() const {return im;}

      const after a function declaration means that the function is not allowed to change any class members (except ones that are marked mutable). So this use of const only makes sense, and is hence only allowed, for member functions. (http://stackoverflow.com/questions/3141087/what-is-meant-with-const-at-end-of-function-declaration)

    • Step 4: 本体之外的函数

      • 操作符重载

        1. inline complex&
        2. complex::operator += (const complex& r)
        3. {
        4. return _doapl (this, r)
        5. }
        • 考虑函数作用在左侧变量,因此存在隐藏函数
        • 右边变量不懂,因此需要const complex & rr <-> right

      - 返回值,仍然需要是复数。如果返回的左侧本来存在,则传引用。

    4. 字符串处理:三大函数

    • Class with pointer member(s)
    • 基本框架
    • 基本功能
      • 初始化
      • 拷贝构造
      • 拷贝赋值
      • 操作符重载
    • 字符串用指针,因为不确定字符串的长短

      • 指针char*指向字符
      • 重新写四个函数(构造、拷贝构造、字符串重载、析构函数)
    • 构造函数

      • 字符串的末尾是
      • 如果初值=0,则分配内存,new char[1]并添加结束符
      • 如果输入有字符串,则需要先测量长度strlen(cstr)+1,在给复制。之后进行数据的拷贝。
      • 析构函数,清理动态分配内存。delete[] m_data
      • 用动态创建的方式和删除,在{...}作用域里面
        1. String* p = new String("hello");
        2. delete p;
    • 拷贝构造函数

      • 初始两个字符串
      • 如果没有自己写的copy函数,会出现泄露。(浅拷贝)

      • 深拷贝

        • String s2(s1);就是对拷贝构造函数的调用
        • String s2=s1;与上一句话含义完全相同,因为s2没有,所以需要重新创造,再完成复制。
    • 拷贝赋值函数

      • 清空delete[] m_data
      • 分配新的m_data = new char {strlen(str.m_data) +1];
      • 赋值strcpy(m_data, str.m_data)
      • 特别要注意监测,检测是否自己赋值给自己
    • output函数

    5. Stack, Heap的应用

    5.1 基本概念

    • Stack:栈,在内存的作用域(scope)的一块内存,
    • Heap:堆,系统提供一块global内存空间,动态取得。
    • 大小括号,是作用域(scope)。
      • c1占用的空间来自stack
      • 函数本身也是scope
      • new来动态从heap中取得,new Complex(3),初值是3,动态(dynamic allocated)得到内存,动态获得就意味着必须delete

    5.2 Stack Object的生命周期

    • 自动被清理
    • 生命在大括号之内

    5.3 static local Object的生命周期

    • 离开大括号,仍然存在
    • 在程序结束后,声明结束

    5.4 Global Object

    • 声明在整个程序结束后

    5.5 Heap Object

    • 正确写法,newdelete搭配使用
    • 上述为错误写法,会出现内存泄漏
    • 内存失去控制,无法将内存还给操作系统

    • Complex* pc = new Complex(1,2),里面的new函数分解为三个动作。定义一个指向Complex的指针。
      • 第一个是分配内存,使用operator new函数来完成。
        • 该函数的整体名称就是operator new
        • operator new函数,进一步调用malloc(n)函数
        • 所需要内存的计算是sizeof(Complex)完成
        • Complex类的设计由实部和虚部两个double完成,因此sizeof(Complex)的结果=8.
      • 第二个动作
        • 第一个动作operator new反馈的结果是指向void的指针。
        • 因此第二个动作将第一个动作的返回值转型。
      • 第三个动作,通过第二步得到的指针,调用Complex::Complex,函数名称与类相同的函数。
        • 构造函数是一个成员函数,因此有list pointer。是pc调用的Comple::Complex函数,因此this就是pc。而pc就是两个doulbe的起始位置。

    • deletenew的反作用。
    • delete pc主要转化为两个动作
      • Step 1: 调用析构函数
      • Step 2:释放内存。

    • 对于String而言,会执行两次delete

    5.6 动态分配内存

    5.6.1 分配一个复数

    • 在VC环境下
    • 会得到8 Byte,即浅绿色部分。
    • 在调试过程中
      • 绿色区域以上(灰色):还会得到以上8组4个Byte。
      • 绿色区域以下(灰色):4个Byte
      • 首尾红色:各1个Byte,Cookie
    • 总共52 Byte。VC给出的是16的倍数,填补设绿色pad,因此给64 Byte。
    • 非调试模式情况下,一个复数的大小是16
    • Cookie主要记录内存块的大小。
      • 00000041 = 64的十六进制40 + 最后一位(1:系统给出去,0:没给出去)

    5.6.2 动态分配

    • 注意pad的占位符

    5.6.3 动态分配的Array

    • 三个复数(8*3灰色部分)
    • 调试部分(32+4)
    • 上下cookie(4)
    • 对于数组,VC的做法添加3,用一个整数记录。

    • 与数组类似

    • 左侧,使用delete [],即array delete,执行三次
      • 编译器知道,删除的是数组,因此执行三次delete
    • 右侧,没有中括号,则执行一次。
      • 编译器不知道,删除的是数组,只执行一次delete
      • 内存泄漏是?!的位置
    • 如果是复数数组,可以使用delete直接删除,因为内部没有再次动态分配。

    6. 复习String类的实现过程

  • 相关阅读:
    Python的单元测试(二)
    Python的单元测试(一)
    未知道——广场式的真匿名交流网站(一)
    xpath提取多个标签下的text
    清空Github上某个文件的历史版本
    如何正确使用日志Log
    使用AWS亚马逊云搭建Gmail转发服务(三)
    玩转Windows服务系列——Windows服务小技巧
    玩转Windows服务系列——服务运行、停止流程浅析
    玩转Windows服务系列——无COM接口Windows服务启动失败原因及解决方案
  • 原文地址:https://www.cnblogs.com/kongww/p/5249283.html
Copyright © 2011-2022 走看看