GeekBand C++ week 2
1.String class (class with pointers)
'=' 赋值方式实现指针copy
copy构造(接受同类作为初值)和 copy赋值(赋值右操作数是同类)不写的话也存在,每bit复制(编译器提供的)。
数据是一个指针,需要时再动态分配空间
接口:构造函数对不同输入操作符重载
析构函数:~+class name, 当一个类对象结束生命时调用(即程序执行离开作用域时)
Big Three: copy构造、copy赋值、析构函数
2.ctor和dtor(构造函数 和 析构函数)
new/delete == malloc()/free() 内存泄漏 动态分配而不释放相应内存
注:“hello”作为初值并不内含'\0',应在初始化时增加
3.class with pointer members必须有copy ctor和copy op=
如果使用default copy ctor或default op=是浅copy,只修改指针的值(因为字符串实际存储位置并不在对象内,使得内存泄露,而内存中仍只有一份数据副本) 出现alias(叠名,多名称指向同一内存空间很危险)
深copy,清除原来的无用数据空间 ,分配新的存储空间,来建立副本。
在copy op=时,注意检查自我赋值(小心alias,提高效率,防止副本在复制前被删除,产生未知bug)
复习 为了避免重载‘<<'时改变方向,要声明为全局函数,以免对象默认作用于左值, ostream要作为左值。
4.Stack(栈)&Heap(堆) scope(作用域)
Stack是存在于某作用域(scope)的一块内存空间会形成一个stack以放置它所接收的参数,local(auto) object,以及返回地址
Heap,或system heap,是指由操作系统提供的一块global内存空间,程序可动态分配(dynamic allocated)从某中获得若干区块(blocks)
5.objects的生命期 何时调用相应析构函数
stack(auto) 在作用域结束之际结束;
static local objects 在整个程序结束时结束;
global objects 在整个程序结束时结束,可视为一种特殊的static object;
heap objects 在它被deleted之际结束。
6.new 先分配memory,再调用ctor
void *mem = operator new(sizeof(type)); //分配内存 内部调用malloc(n)
pc = static_cast(mem); //类型转换
pc->type::type(arg); //构造函数 => Complex::Complex(pc, 1, 2) pc是ctor作为成员函数的this指针
7.delete 先调用dtor,再释放memory
type::~type(pc); //析构函数
opetator delete(pc); //释放内存
8、动态分配所得的内存块(memory block)
(system)cookie +(Debug header 32bytes+) 所需内存 + (no man land 4 +)(pad 4*n +)(system)cookie
cookie(4byte)以标志分配区块大小和状态
因为VC中都为16的倍数,所以cookie最后一个byte的低四位总是0,因此可以用最低位借位表示1已分配和0未分配
动态分配所得array(array new搭配array delete)
(system)cookie +(Debug header 32bytes + )元素数标志(int 4byte) + 所需内存 + (no man land 4 +)(pad 4*n +)(system)cookie
未搭配情况,会使得析构函数不正确调用,使得array内动态分配去泄露
补充内容
1)static 通过传入this pointer使得同一个成员函数处理不同的对象,而不会出现多个重复函数单一处理各自对象,静态函数没有this pointer只能处理静态数据。static数据全类唯一,并通过类外赋值完成初始化,初始化要在实现中进行。静态函数既可被对象作为成员函数调用,也可以作为类成员函数直接调用。
静态函数和静态数据成员都不是对象成员而属于类的静态成员,静态成员访问可以在类中进行,对非静态成员则只能通过对象。
singleton模式,以static方式(类中副本单一)
2)cout 进行了大量的<<操作符的重载,一种ostream
3)class template 对同一实现成员不同的情况进行代码重用 template 使用时:class
function template 对同一实现成员不同的情况进行代码重用 template 使用时不必显式声明,编译器对function template可以进行参数推到(argument deduction)
4)namespace + 名称,把所有用到的东西封装在一个namespace里,以免重名产生的错误。多文件写std在编译时会被组合到一起。
使用方式
using directive 将封装完全打开,e.g. using namespace std;
using declaration 逐条声明 e.g. using std::cout;
直接写出全名 e.g. std::cin >> ...;
面向对象编程 探讨类和类之间的关系
1.Inheritance(继承) is a关系
三种继承方式
:public 父类(Base)的数据是被子类(Derived)完整继承的,子类中含有父类的成分;和虚函数搭配体现价值;
函数的继承继承的是调用权;在子类对象中调用父类函数是一种常见现象。把父类的强关联函数,放到具体子类中实现,建立Template Method实现。一般性操作建立Application framework,使专业人员能集中处理核心问题。成员函数内调用函数仍由this作为参数调用。
构造由内而外:Derived::Derived(...): Base() {...};
构造由外而内:Derived::~Derived(...) {...~Base() };
base class 的dtor必须是virtual,否则会出现undefined behavior。
:protected
:private
Inheritance(继承) with virtual functions(虚函数) override(仅用于虚函数被重新定义,覆写)
non- virtual function: 你不希望derived class override它
virtual function:你希望derived class override它,且你对它已有默认定义 virtual 标识符
pure virtual function:你希望derived class一定要override它,你对它没有默认定义 virtual 函数声明 =0
Inheritance + Composition 关系下的构造和析构
//Derived 同时含有 Composition的component情况
Derived::Derived(...):Base(), Component() {...} 构造由内而外
Derived::~Derived(...):{...~Component(), ~Base()}; 析构由外而内
Delegation + Inheritance 尤其用在user interface
Subject(数据存储)-建立vector-> Observer(数据展示)
多种展示方式作为Observer的子类。
2.Composition(复合) has a关系,引用其它结构/类型作为成员。
Adaptor:通过复合一个强功能的结构,然后开放部分功能(即构建一个adaptor),来使之符合使用要求。
Container -> Component
Container构造时由内而外,先调用Component的default构造函数,然后才执行自己;
Container析构时由外而内,首先执行自己,然后才调用Component的析构函数。
以上都是编译器补充的,实际构造析构并不需要明文写出。
如果编译器提供的默认方式不合意,请自行编写构造函数。
3.Delegation(委托)/Composition by reference
用指针的方式实现has,而不是直接含有相应对象,需要时指针才有所指(不同步)。
pointer to implementation/Handle & Body 用指针指向实现,可以方便的切换实现,亦称编译防火墙。
reference counting 内容共享特性,特别注意,共享时修改要copy on write即修改时提供新的副本给对象进行修改。
4.设计模式
容器里放的东西一定要一样大小,因此推荐指针作为容器参量。add 以指向父类的指针进行操作,使得两种子类都可以进行add 操作。
现在去创建未来的对象,每一个子类创建自己,被之前的父类看到,子类把自己的原型置入父类的相应空间。