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++
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
- 布局
```cpp
#ifndef __COMPLEX__
#define __COMPLEX__
#include <cmath>
//forward declarations(前置声明)
class ostream;
class complex;
complex& __doapl(complex* ths, const complex& r);
//class declarations(类-声明)
class complex
{
... ...
}
//class definition(类-定义)
complex::function ...
#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;
}
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;
}
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
``cpp
Complex* 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
- 动态分配所得的array
- array new一定要搭配array delete
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 templateclass 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
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
通过子类的对象,调用父类的函数。
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关系下的构造和析构
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);
};
13. Delegation(委托)+Inheritance(继承)
13.1 Composite(组合设计模式)
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