1. 简介

1.1 学到

  • 以良好的方式编写c++ class(基于对象) class without pointer members(Complex) class with pointer members(String) 带指针和不带指针的
  • 掌握class之间的3种关系-继承、复合、委托(面向对象)
  • C++目前有两个大版本,C++98(1.0)和C++11(2.0)

    1.2 书籍

  • 《C++ Primer》百科级 最新是第五版 作者参与了C++第一个编译器的实现。

  • 《The C++ Programming Language》最新是第四版,作者是C++之父,目前还没有中文
  • 《Effective C++》最新是第三版,告诉你什么改写,什么不该写
  • 《The C++ Standard Library》这本书已经有中文版 《STL源码剖析》市面上少有同类书籍

2. 头文件和类的声明

2.1 C VS C++

image.png

2.2 数据与函数

Classes的两个经典分类:

  • Class without pointer member(s) —>complex 实部,虚部 加减乘除,共轭,正弦
  • Class with pointer member(s)—>string 字符(s)(其实是个ptr,指向一串字符串) 拷贝,输出,附加,插入

    2.3 Object Based(基于对象)vs. Object Oriented(面向对象)

    Object Based:面对的是单一class的设计
    Object Oriented:面对的是多重classes的设计,classes和classes之间的关系。

    2.4 Header(头文件)

  • 防卫式声明 ```cpp

    ifndef COMPLEX

    define COMPLEX

… …

endif

  1. - 布局
  2. ```cpp
  3. #ifndef __COMPLEX__
  4. #define __COMPLEX__
  5. #include <cmath>
  6. //forward declarations(前置声明)
  7. class ostream;
  8. class complex;
  9. complex& __doapl(complex* ths, const complex& r);
  10. //class declarations(类-声明)
  11. class complex
  12. {
  13. ... ...
  14. }
  15. //class definition(类-定义)
  16. complex::function ...
  17. #endif
  • class 声明(declaration)

    //class head
    class complex
    //class body
    {
    public:
      complex (double r=0, double i = 0)
          : re (r), im(i)
      { }
      //有些函数在body之外定义
      complex& operator += (const complex&);  //这只是个声明
      //有些函数在body直接定义
      double real () const {return re;}
      double imag () const {return im;}
    private:
      double re, im;
    
      friend complex& __dopal (complex*, const complex&);
    }
    
  • 模板

    template<tyepname T>
    class complex
    {
    public:
      complex (T r=0, T i = 0)
          : re (r), im(i)
      { }
      complex& operator += (const complex&);
      T real () const {return re;}
      T imag () const {return im;}
    private:
      T re, im;
    
      friend complex& __dopal (complex*, const complex&);
    }
    

    3. 构造函数

    3.1 inline(内联)函数

    函数若在class body内定义完成,便自动成为inline候选人。 最后函数是不是成为inline,最后由编译器来决定。

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

    3.2 constructor(ctor,构造函数)

    使用初值列效率比赋值的写法,效率高 ```cpp //初值列 comlpex(double r=0, double i=0)//default argument(默认构造函数) : re(r), im(i)//initializationi(初值列,初始列) { }

//赋值 comlpex(double r=0, double i=0) { re = r; im = i}//assignments(赋值) { complex c1(2,1); complex c2; complex* p = new complex(4); … … }

<a name="ecOJ7"></a>
#### 3.3 ctor(构造函数)可以有很多个-overloading(重载) 
```cpp
double real() const {return re;}    
void real(double r) {re=r;}

real函数编译后的实际名称可能是:

?real@Complex@@QBENXZ
?real@Complex@@QAENABN@Z

注意:重载如果如冲突则不可,例如 空参数的构造函数,和有初始值的构造函数 冲突了 就不能同时存在。

4. 参数传递与返回值

4.1 constructor(ctor,构造函数)被放在private区

//Singleton 单例模式
class A{
public:
    static A& getInstance();
    setup() {... ...}
private :
    A();
    A(const A& rhs);
    ... ... 
}

A& A::getInstance()
{
  static A a;
  return a;
}

//调用方式
A::getInstance().setup();

4.2 const member functions(常量成员函数)

double real () const { return re;}
double image () const {return im;}

这两个函数只是取值,不会改变类中的数据内容,则函数可以加上const const在括号后面 .
如果上面两个函数没有加const,使用者如果按下面的写法,编译会出错.

{
    const complex c1(2, 1);
    cout << c1.real();
    cout << c1.image();
}

4.3 参数传递: pass by value vs. pass by reference(to const)

参数传递尽量用pass by reference(to const)

`complex& operator += (const complex&);`

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

尽量用引用

`complex& operator += (const complex&);`

返回局部变量时不可以用引用。

4.5 friend(友元)

friend complex& __doapl(complex*, const complex&);`

相同class的各个objects互为friends(友元)

int func(const complex& param)
{ return param.re + param.im;}

{
    complex c1(2, 1);
    complex c2;
    c2.func(c1);
}

5. 操作符重载和临时对象

5.1 operator overlaoding(操作符重载-1,成员函数) this

inline complex&
__doapl(complex* ths, const complex& r)
{
  ths->re += r.re;
  ths->im += r.im;
  return *ths;
}

// += 操作符重载
//任何成员函数都有一个隐藏的 this

inline complex&//此函数的返回类型可以为void,
//但是使用者可能 c3 += c2 += c1这样使用,如果返回void,编译不能通过
complex::operator += (const complex& r)
{
  return __doapl(this, r);
}

{
  complex c1(2, 1);
  complex c2(5);
  c2 += c1;
}

5.2 return by reference语法分析

传递者无需知道接受者是以reference形式接收

inline complex&//接收者
__dopal(complex* ths, const complex& r)
{
  ... ...
  return *ths;//传递者
}

5.3 temp object(临时对象) typename();

complex();
{
  int(7);

  complex c1(2, 1);
  complex c2;
  complex();//执行完此行以后,生命就结束
  complex(4, 5);

  cout << complex(2);
}
//函数不可能return by reference,
//因为他们返回的必定是个local object
inline complex
operatior - (const complex& x)
{
  return complex(-real(x), -imag(x));
}

{
  complex c1(1, 2);
  complex c2;
  cout << -c1;
  cout << +c1;
}

5.4 operator overloading(操作符重载),非成员函数

+ - == !=

<<必须写成全局函数

7. 拷贝构造、拷贝赋值、析构

7.1 Big Three

三个特殊的函数:ctor和dtor(构造函数和析构函数) 赋值
class with pointer members必须有copy ctor和copy op=

7.2 copy ctor(拷贝构造函数)

inline
String::String(const String& str)
{
  m_data = new char[strlen(str.m_data) + 1];//直接取另一个object的private data
  //兄弟之间互为friend-->相同class的各个objects互为friends(友元)
  strcpy(m_data, str.m_data);
}

{
  String s1("hello");
  String s2(s1);
//String s2 = s1;
}

copy op=.png

7.3 copy assignment operator(拷贝赋值函数)

一定要检测自我赋值,不仅是为了效率,更为了正确

inline String&
String::operator=(const String& str)
{
  if(this == &str)//检测自我赋值(self assignment)
    return *this;

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

self assignment.png

8. 堆栈与内存管理

8.1 output函数

#include <iostream.h>
ostream& operator << (ostream& os, const String& str)
{
  os << str.get_c_str();
  return os;
}

8.2 statck(栈),heap(堆)

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

在函数本身(function body)内声明的任何变量,其所使用的内存块都取自上述stack。

  • heap,或者叫system heap,是指由操作系统提的一块global内存空间,程序可动态分配(dynamic allocated)从其中获得若干区块(blocks)

    8.3 生命周期

  • statck objects的生命期

生命在作用域(scope)结束时结束。这种作用域内的object称为auto object,因为它会被自动清理。

  • static local object的生命期

生命作用域结束后任然存在,直到整个程序结束。

  • global objects的生命期

其生命在整个程序结束之后才结束。也可以把它视为一种static object,其作用域是整个程序。

  • heap objects的生命期

    class Complex {... ...};
    ...
    {
    Complex* p = new Complex;
    ...
    delete p;
    }
    

    p所指的便是heap object,其生命在它被deleted之后结束。

    class Complex {... ...};
    ...
    {
    Complex* p = new Complex;
    }
    

    以上出现内存泄漏(memory leak),因为当作用域结束,p指向的heap object任然存在,但指针p的生命却结束了,作用域之外在也看不到p(也就没有机会delete p)。

    8.4 new与delete

  • new:先分配memory,在调用ctor ``cppComplex* pc = new Complex(1, 2);`
    编译器转化为:

Complex * pc;

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


- delete:先调用dtor,在释放memory
```cpp
String* ps = new String("Hello");
...
delete ps;

编译器转化为:
String::~String(ps);//析构函数
operator delete(ps);//释放内存(其内部调用free(ps))

8.5 动态分配所得的内存块(memory block)

  • 动态分配所得的内存块(memory block) in VC

memory block in vc.png

  • 动态分配所得的arrayarray.png
  • array new一定要搭配array delete

array delete.png

10. 类模板、函数模板及其他

10.1 static

静态的成员函数没有this
静态成员函数只能调用静态成员变量,两种调用方式:class name 或 onject

10.2 把ctors放在private

  • Singleton

    class A{
    public:
    static A& getInstance(){return a;}
    setup(){...}
    private:
    A();
    A(const A& rhs);
    static A a;
    ...
    }
    //调用方式
    A::getInstance().setup();
    
  • Meyers Singleton ```cpp class A{ public: static A& getInstance(); setup(){…} private: A(); A(const A& rhs); static A a; … }

A& A::getInstance() { static A a; return a; } //使用了才创建

10.3 function template<br />c++标准库中算法的实现都是用函数模板实现的。<br />编译器会对function tempalte进行引数推导(argument deduction)
```cpp
template <class T>
inline
cosnt T& min(const T& a, const T& b)
{
  return b<a ? b:a;
}

引数推导的结果,T为stone,于是调用stone::operator<
class stone
{
public:
  stone(int w, int h, int we): _w(w), _h(h), _weight(we) {}
  bool operator< (const stone& rhs) const {return _weight < rhs._weight;}
private:
  int _w, _h, _weight;
};

10.4 namespace

使用:

  • using namespace std;//using directive 全部打开
  • usging std::cout;//using declaration 部分打开

11. 组合与继承

11.1 Object Oriented Programming, Object Oriented Design OOP OOD

  • Inheritance(继承)
  • Composition(复合)
  • Delegation(委托)

    11.2 Compositioin(复合),表示has-a

    ```cpp template class queue{ … protected: deque c;//低层容器 public: //以下完全利用c的操作函数完成 bool empty() const {return c.empty();} size_type size() const {return c.szie();} reference front() {return c.front();} reference back() {return c.back();} //deque是两端可进出,queue是末端进前端出(先进先出) void push(const value_type& x) {c.push_back();} void pop() { c.pop_front();} }
![composition has-a.png](https://cdn.nlark.com/yuque/0/2022/png/1765525/1647931274754-9a111e7e-af09-4b86-b922-76f85ab54388.png#clientId=ufab372e7-0f09-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=uc7388531&margin=%5Bobject%20Object%5D&name=composition%20has-a.png&originHeight=64&originWidth=362&originalType=binary&ratio=1&rotation=0&showTitle=false&size=29313&status=done&style=none&taskId=u66806822-3ef4-48d2-adc7-edd7433c787&title=)<br />Adapter 适配模式(变压器)deque的功能已经很强大了,queue将deque包进来,只开放了六个功能;这样表现出一种设计模式->适配模式 <br />ctors and dtors:<br />![composition cotr dctor.png](https://cdn.nlark.com/yuque/0/2022/png/1765525/1647931325901-e25d7165-5cba-4114-980c-0d9f5fd56938.png#clientId=ufab372e7-0f09-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=ue7a9c61e&margin=%5Bobject%20Object%5D&name=composition%20cotr%20dctor.png&originHeight=194&originWidth=625&originalType=binary&ratio=1&rotation=0&showTitle=false&size=109885&status=done&style=none&taskId=uef499e80-692e-46b9-b619-f215c037abf&title=)<br />构造由内而外 Container的构造函数首先调用Component的default构造函数,然后才执行自己。 Container::Container(...) : Component() { ... }; <br />析构函数由外而内 Container的析构函数首先执行自己,然后才调用Component的析构函数。 Container::~Container(...) {... ~Component()}; 
<a name="LOYgW"></a>
#### 11.3 Delegation(委托).Composition by reference
![delegation.png](https://cdn.nlark.com/yuque/0/2022/png/1765525/1647931397489-ce325166-722c-4e0a-b4cf-7ecff283c52d.png#clientId=ufab372e7-0f09-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=u5c0f00cd&margin=%5Bobject%20Object%5D&name=delegation.png&originHeight=66&originWidth=371&originalType=binary&ratio=1&rotation=0&showTitle=false&size=29408&status=done&style=none&taskId=uc9071a3e-c6fe-466a-ab4e-c693983c80a&title=)
```cpp
//file String.hpp
class StringRep
class String {
public:
  String();
  String(const String* s);
  String(const String& s);
  String& operator = (cosnt String & s);
  ~String();
  ...
private:
  StringRep* rep;//pimpl
};

//file String.cpp
#include "String.hpp"
namespace{
class StringRep{
friend class String;
  StringRep(cosnt char* s);
  ~StringRep();
  int cout;
  char* rep;
};
}

String::String(){...}
...

接口,编译防火墙。

11.3 Inheritance(继承),表示is-a

ctor dtor(Inheritance).png
bass class的dtor必须是virtual,否则会出现undefined behavior
如果bass class的dtor函数不是virtual,析构就不会由外而内

  • 构造由内而外Derived的构造函数首先要调用Base的default构造函数,然后才执行自己。 Derived::derived(…) : Base() {…}
  • 析构由外而内 Derived的析构函数首先执行自己,然后才调用Base的析构函数。 Derived::~Derived(…) {… ~Base()};

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

  • non vitual函数:你不希望derived class重新定义(override,覆写)它。
  • virtual函数:你希望derivedclass重新定义(overried,覆写)它,且你对它已有默认定义。
  • pure virtual函数:你希望derived class一定要重新定义(overried,覆写)它,你对它没有默认定义。
    class Shape{
    public:
    virtual void draw() const = 0;        //pure virtual
    virtual void error(const std::string& msg);    //impure virtual
    int objectID() const;                    //non virtual
    ...
    };
    

12. 虚函数与多态

12.1 Inheritance(继承)with virtual

设计模式 Template Method
template method.png
通过子类的对象,调用父类的函数。
myDoc调用的是CDocument的OnFileOpen(),执行到Serialize()时,会跑到CMyDoc类的Serialize函数。
Serialize函数被延缓到子类中实现,将这种做法叫做: Template Method。

#include <iostream>
using namespace std;

class CDocument
{
public:
  void OnFileOpen()
  {
    //这是个算法,每个cout输出代表一个实际动作
    cout << "dialog ..." <<endl;
    cout << "check file status ..." << endl;
    cout << "open file ..." << endl;
    Serialize();
    cout << "close file ..." << endl;
    cout << "update all view ..."  endl;
  }

  virtual void Serialize() {};
};

class CMyDoc : public CDocument
{
public:
  virtual void Serialize()
  {
    //只有应用程序本身才知道如何读取自己的文件(格式)
    cout << "CMyDoc::Serialzie()" << endl;
  }
};

int main()
{
  CMyDoc myDoc;//假设对应[File/Open]
  myDoc.OnFileOpen();
}

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

20200409181632395.png

12.3 Delegation(委托)+Inheritance(继承)

class Subject
{
  int m_value;
  vector<Observer*> m_views;
public:
  void attach(Observer* obs)//注册Observer
  {
    m_view.push_back(obs);
  }
  void set_val(int value)
  {
    m_value = value;
    notify();
  }

  void notify()//通知
  {
    for(int i=0; i<m_views.size(); ++i)
      m_views[i]->update(this, m_value);
  }
};

//有n个不同的人在看一份数据  
class Observer
{
public:
  virtual void update(Subject* sub, int value);
};

observer.pngobserver-n-difference.png

13. Delegation(委托)+Inheritance(继承)

13.1 Composite(组合设计模式)

20200411013617562.png

class Component
{
  int value;
public:
  Component(int val){ value = val;}
  virtual void add(Component*){}
};

class Primitive : public Component
{
public:
  Primitive(int val) : Component(val){}
};

class Composite : public Component
{
  vector<Component*> c;
public:
  Composite(int val) : Component(val){}

  void add(Component* elem){
    c.push_back(elem);
  }
};

13.2 Prototype(原型设计模式)

创建未来类的对象
下划线表示static
-表示private
#表示protected

20200411015345452.png

20200411020904493.png
20200411020940238.png