《C++面向对象高级编程(上)》侯捷C++

1.简介  

 基于对象(Object Based)

  • class without pointer members --Complex
  • class with pointer members -------String

面向对象(Object Oriented)

  • 学习Classes之间的关系
    • 继承
    • 复合
    • 委托

C++历史

C++有很多版本,如 C++98,C++03,C++ 11,C++14

学习C++(其他语言也是)可以分为两个部分:

  • C++语言
  • C++标准库

经典的书:

  • C++ Primer (C++第一个编译器的作者写的)
  • C++ Programming Language (C++之父写的)
  • Effective C++ (专家写的,以条款方式写的)
  • The C++ Standard Library(写的C++的标准库)
  • STL源码剖析(较深入的将标准库)

2.头文件与类的申明

 防卫式申明!

#ifndef __COMPLEX__
#define __COMPLEX__
....
#endif

inline内联函数 

函数在class本体里定义就形成了inline,inline像宏一样,函数如果是inline的,就会很快,所以所有的函数都写成inline是最好的,然而编译器没有能力把所有函数都做成inline,一般来说程序复杂,编译器就不能做成inline。inline function最后能不能做成inline看编译器的能力。

不在本体里定义inline的要加inline:

inline double imag(const complex& x)
{
...
}

访问级别(access level)

  • public :外部能看到的
  • private:只有自己能看到的,一般只有内部函数处理的数据要用private
  • protect:受保护的

3.构造函数

比较熟悉,略

4.参数传递与返回值

定义函数 确定是否添加 const

  • 对不会改变的参数前加const 避免误用修改了参数值
  • 也会在整个函数前 加const

参数传递 (值传递 vs.引用传递)(pass by value vs. pass by reference(to const))

  • by value :是数据整包传过去,数据有几个字节就传几个字节
  • by reference(引用):带&,传引用相当与传指针的速度。
  • by reference(to const):没有const时,传给对方的数据,对方可能修改后影响我,如果不想自己受到影响,就要加上const,表示此引用不会该改变数据,对方改变就会出错。

     良好的习惯:参数传递尽量的传引用,速度快

返回值传递(return by value vs. return by reference(to const))

基本同上

友元 friend

熟悉,略

5.操作符重载和临时变量

熟悉,略

6.复习Complex类的实现过程

熟悉,略

7.三大函数:拷贝构造,拷贝复制,析构(big three)

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

//析构函数
inline
String::~String()
{
    delete[] m_data;
}

 class with pointer members 必须有 copy ctor和copy op=

否则使用default copy ctor 或者 default op= 就会造成内存泄漏

默认拷贝 即是 浅拷贝!

如下图,b=a后,两者指向一个,容易出错,同时“world” memory leak!

//拷贝赋值函数
inline
String& String::operator=(const String& str)
{
    //检测自我赋值 (self assignment)
    if(this== &str)
        return *this;
    //上面不写会出错! 因为是同一个指针指向同一个 
    //具体直接看下面的图!

    delete[] m_data;
    m_data = new char[ strlen(str.m_data) +1];
    strcpy(m_data,str.m_data);
    
    return *this;
}

自我赋值!不止为了效率,也为了正确率! 

8.堆、栈与内存管理

  • 左移 “<<”运算符 只能写成 全局函数,不可以写成成员函数!
  • Stack (栈)
  • 是存在于某作用域(scope)的一块内存空间(memory space)。例如当你调用函数,函数本身即会形成一个stack用来放置它所接收的参数,以及返回地址
  • 在函数本体(function body)内声明的任何变量,其所使用的内存块都取自上述stack。
  • 编译器自动开辟和释放

 

  • Heap(堆)
  • 或称 system heap,是指由操作系统提供的一块global内存空间,程序可动态分配(dynamic allocated)从某中获得若干区块(blocks)
  • new 开辟 和 delete 释放
  • 手动
class Complex{....};
...
...
{
    // stack
      Complex c1(1,2);

    // heap
      Complex* p = new Complex(3);
  • stack objects的生命期  : 作用域(scope)结束时自动结束
  • stack local objects的生命期 : 程序结束 即一直存在   命名时前加:关键字 static
  • global objects的生命期:也是程序结束,定义为全局变量
  • heap objects的生命期: delete 决定,如果不delete 就会memory leak !!浪费内存!很严重的问题!

因此主要讲解heap的 new 和 delete

  • new:先分配 memory 在调用ctor(构造函数) 
  • delete : 先调用dtor(析构函数) ,再释放memory
  • 细节如下: 可以看到malloc和free只是其中的一个步骤,在搞内存时!

动态分配所得的内存块(memoty block)in VC(编译器)

侯捷大师的讲解!!

要素过多:

  • 带灰色的是调试模式,左边是“复数” 右边是“String”
  • 申请的字节数 必须是16的倍数,因此pad部分称为 填充物!
  • 头尾红色的是cookie(曲奇小饼干)用来标记长度 如41H 其实是64字节;多出来的1H表示获得状态,free时为0!

 动态分配所得的array   基本同上!

array new 要搭配 array delete 

  • 如果没有搭配 就是左边的情况 这里只调用一次dtor ,然后就free了内存 所以造成了 下面2个的内存泄露
  • 而像complex这种dtor什么也没干的  即使写错了也没事,因为它的dtor什么也没干,但这样很容易搞混 所以都要带!!

9.复习String类的实现过程

class String
{
public:
    String(const char* cstr =0 );
    //带指针 考虑Big three
    //拷贝构造
    String(const String& str);
    //拷贝赋值
    String& operator=(const String& str);
    //析构函数
    ~String();
    char* get_c_str() const
    {
        return m_data;    
    }
private:
    char* m_data;
};
inline
String::String(const char* cstr =0 )
{
    if(cstr)    
    {
        m_data = new char[strlen(cstr)+1];
        strcpy(m_data,cstr);
    }
    else{
        m_data= new char[1];
        *m_data = '\0';
    }
}

inine
String::~String()
{
    delete[] m_data;
}

inline
String::String(const String& str)
{
    m_data = new char[strlen(str.m_data)+1];
    strcpy(m_data,str.m_data); 
}

inline
String& String::operator=(const String& str)
{
    if(&str==this)
    {
        return *this;
    }
    delete[] m_data;
    m_data= new char[strlen(str.m_data)+1];
    strcpy(m_data,str.m_data);
    return *this;
}

 

10.扩展补充:类模板,函数模板 及其他

进一步补充:static

静态数据 :需要类内申明 ,类外定义。

静态函数 没有this指针。有如下的两种调用方式。

 

进一步补充:把ctors放在private类

这里的Singleton和Meyers Singleton是两种设计模式

Singleton(单例)只存在一个,但上面的定义存在一个问题,即使不调用 也一直存在,比较浪费。于是

进一步补充:cout

 继承自 ostream

 进一步补充:class template 类模板

注意关键字 typename

进一步补充:function template 函数模板

 关键字 class  还会自动推导

 进一步补充:namespace(命名空间)

11、组合和继承

面向对象的编程

  • 继承、复合、委托

Compisition(复合) 表示has -a

  • Adapter是一种设计模式 (比较强的东西 修改 以适应当前的 任务)
  • deque是双向队列,queue是队列,因此这里的六个函数,均能直接用deque的,不用自己重新写。
  • 注意 其右上角的符合表示 实心菱形

内存的角度看 复合 40=16*2+4+4

Composition(复合)关系下的构造和析构

  • 注意构造 调用Componue(组件)的默认构造,如果需要调用其他的需要自己去写。
  • 以下从内存的角度看

 Delegation(委托)/Composition by reference

  • 用指针指向调用的class 也将任务委托过去
  • 通过指针 叫 by reference 没有 by pointer的叫法
  • 注意右上角符号 的空心,代表指针没那么实在,只有用到的时候才会调用,因此生命周期不一致
  • 左右两部分 的设计模式叫Handle/Body!非常有名,因为增加了Body的弹性,而Handle面向用户看不出来变化,也叫做编译防火墙!!很牛的样子
  • 这里Body在做“引用计算”,看左下角的具体过程,有三个共享“hello”字符串

Inheritance(继承) ,表示is-a

  • 右侧图示表示
  • 可以继承数组,而最有价值的点是虚函数!!!!

 Inheritance(继承)关系下的构造和析构

  • 依然是从内存的角度看,注意其先后顺序,和复合很像

 12.虚函数与多态

  • 函数的继承:继承的是调用权

Inheritance(继承)with virtual functions(虚函数)

  • non-virtual函数:你不希望derived class 重新定义(override 覆写)它
  • virtual函数:你希望derived class重新定义(override,覆写)它,且你对它已有默认定义
  • pure virtual函数:你希望derived class一定要重新定义(override 覆写)它,你对它没有默认定义

Inheritance(继承)with virtual

  • 左侧是父类,framework框架(可能很久前就写好的,一些常规性操作)
  • 右侧是子类,后续自己实现的
  • 大名鼎鼎的设计模式 Template Methos(java里的概念)
  • 中间的粗灰线是 函数的调用过程(灰色框里是编译器的解释)
  • 右下角的 子类对象调用父类函数!

Inheritance+Composition关系下的构造和析构

Delegation(委托)+Inheritance(继承)

  • 功能最强大的组合
  • 为了解决下面这样的问题,同一个东西的多种显示

  • 左边放数据,右边是观察者
  • 让左边可以有很多的右边,使用者可以很多的右边
  • 开辟一个容器,用Observer指针可以指向很多个右边
  • 右边用虚函数 可以让子类 可以干很多事情

13.委托相关设计

Delegation(委托)+Inheritance(继承)

  • ​​​​用图代表代码

  • 想用Primitive(基础物)和Composite防在同一个容器中

  • 创建一个父类,二者均继承,用指针委托

问题:我现在要创建一个未来的Class对象怎么办?

Prototype

  • 带下划线表示静态 LSAT
  • 很复杂的一个例子,没太听懂,但老师一直拍案叫绝,以后要用的时候再细细琢磨吧

​​​​​​​

至此,第一系列的课程结束!

不得不说侯捷老师不愧为大师,很多知识点讲的深入浅出,拓展度也很大,C++果然很复杂。

路漫漫其修远兮,吾将上下而求索。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值