Week3 notes
A.面向对象编程,面向对象设计
composition,复合表示has-a
template>
class queue{
…
protected:
Sequencec; //底层容器
Public:
//以下玩完全利用c的操作函数完成
boolempty() const {return c.empty();}
size_typesize() const {return c.size();}
referencefront() {return c.front();}
referenceback() {return c.back();}
//
void push(constvalue_type& x) {c.push_back(x);}
void pop(){c.pop_front();}
};
a拥有b就叫composition,以上是一个特例,a拥有b,a所有的功能都用b来实现,但现实生活中确实有这种东西,deque的意思是两端都可以进出。所以deque得功能比queue强大。这里做的是改装一下,比如最后的pop方法,换了一个面貌出现,说不定deque有一百个功能,但现只包含了6个功能,而且名字可能变了,这是一种设计模式,叫做adaptor。变压器,改造,适配,现在手上有一个东西完全满足要用的功能,只是可能接口不同,名字不同,所以就改造一下,这里queue就是adaptor.
从内存的角度来理解。
40
Template
Class queue{
Protected:
Dequec;
}
16*2+4+4
template
class deque{
protected:
Itrstart;
Itr finish;
T** map;
Unsigned intmap_size;
}
4*4
template
struct Itr{
T* cur;
T*
T*
T*
}
复合关系下的构造和析构:
构造由内而外,container的构造函数首先调用component的default构造函数,然后才执行自己。
Container::container(…): component(){…};
析构由外而内,container的析构函数首先执行自己,再调用component的析构函数。
Container::~container(…) {…~component()};
这些部分编译器自己会加上去,形成合理。构造函数是默认的这一个,如果这一个不符合的话,就要我们自己写,自己调用内部的构造函数,输入都要自己写,但是析构函数只有一个所以不需要。
I’?
Delegation委托,composition byreference.ZX
String.hpp
Class StringRep;
Class String{
Public:
String();
String(constchar* s);
String(constString& s);
Stringoperator=(const String& s);
~String();
private:
StringRep*rep; //pimpl
};
#include “String.hpp”
namespace{
class stringRep{
friend class String;
StringRep(constchar* s);
~StringRep();
intcount;
char*rep;
}
};
两个类之间用指针相连就叫委托,如果有了一个外部的就有一个内部,就叫composition,现在用指针相连,只有在要用到右边的时候才用到,叫委托,也叫composition by reference,具体的实现都在右边做,当左边要用到的时候调用右边的服务,这种写法非常有名,pointer to implementation. Handle/body为什么这么有名呢?因为我们如果把类都写成这样的话,左手边都不需要换,字符串如果要怎么变这个指针可以指向不同的实现类,右边怎么变动都不影响左边,也就不影响客户端,这个手法也叫编译防火墙,左边都不需要管右边。
但用指针三个人共享同一个hello要注意abc互相不知道他们引用同一个,当a改变的时候要单独给你一份改,叫copy on write.
Inheritance继承,表示is-a
Struct _List_node_base
{
_list_node_base*Mnext;
Listnodebase*_M_prev;
};
template
struct _List_node
:public_List_node_base
{
_Tp_M_data;
}
c++有三种继承,使用public继承就是is-a,如果你用public继承但两个类的关系不是is-a,将来有可能出错。
继承关系下的构造和析构。Derived派生类。从内存的角度来看子类的对象里头有一个父类的base part,base class的dtor必须是virtual的,否则会出现undefined behavior
构造要由内而外,
derived的构造函数首先调用base的default构造函数,然后才执行自己。
Derivevd::derived(…): base(){…};
析构由外而内
derived的析构函数首先执行自己,然后才调用base的析构函数。
Derived::~derived(…) {…~base()};
继承要搭配虚函数virtual function
B.虚函数与多态
虚函数与继承
当我们使用继承的时候要搭配虚函数,非虚函数non-virtual function是你不希望derived class重新定义(override,复写)他。
虚函数是你希望derived class重新定义(override)它已有的默认定义。Override一定被用在虚函数被重新定义。
Pure virtual函数是你希望derived class一定要重新定义它,你对他没有默认定义。和虚函数的区别是你根本没有定义。
Class Shape{
Public:
Virtualvoid draw() const = 0;//pure virtual
Virtualvoid error(const std::string& msg);//impure virtual
IntobjectID() const;//non-virtual
…
}
class Rectangle: public Shape{…};
class Ellipse: public Shape {…};
CDocument::
OnFileOpen(){
…
Serialize()
…
}
读的动作是serialize()
我们写我们自己的子类
class CMyDoc:
publicCDocument{
virtualSeriallize() {…}
}
main(){
CmyDocmyDoc;
myDoc.OnFileOpen();
}
通过子类的对象调用父类的函数。函数的全名是CDocument::OnFileOpen(&myDoc);
23个设计模式之一Template
Method,模板方法,将一些函数延缓写出来,我先帮你想好了你要写一个应用程序你要有哪些功能,很多功能都是相同的,我先帮你预设好,具体的部分留到你自己的子类中去重构他。这里的CDocument中的OnFileOpen是应用框架,Application Framework,十多年前MFC Microsoft Foundation classes
继承和复合关系下的构造和析构
如果子类继承父类之外又有一个component,那哪个构造函数先被调用呢?
如果父类中有一个component的话又是怎么样呢?
1父类,component,子类
委托加继承关系
Office软件中可以开多个窗口看同一个东西。或者是同一份数据可以用三种不同的形式观察。要想有这种功能,有两个CLASS一种是存储,一种是表现。
Class Subject{
Intm_value;
Vectorm_views;
Public:
Voidattach(Observer* obs){
M_views.push_back(obs);
}
void set_val(intvalue){
m_value = value;
notify();
}
void notify(){
for(int I = 0; i< m_value.size();i++)
m_views[i]->update(this, m_value);
}
};
class Observer{
public:
virtualvoid update(Subject*sub, int value) = 0;
};
左边是有一个delegation的关系,左边要有注册和注销的功能。Attach实现注册功能。
C.委托相关设计
委托加继承
Composite
现在要写一个file system,有目录,有文件,目录里面可以放文件,目录还可以放到其他目录里。window system,大窗口里面有小窗口。那应该使用哪些class,应该使用什么将他们联系起来。
目录系统。
Primitive,composite可以放左边的primitive,也可以放自己,如果左边和右边写一个父类,这样两边都是is-a component,所以就可以写成c:vector,不能放对象,要放指针,右边这个东西是一个组合物,所以他应该具有一个函数add(e:component*),因为它又可以加左边的东西,,也可以加右边的东西,这样两个东西都可以接受。发现有很多解法都是要用到两把武器。这种解法叫做composite是23个设计模式中的一个。
Class primitive: public Component{
Public:
Primitive(intval): Component(val) {}
};
class Component{
intvalue;
public:
Component(intval) {value = val;}
Virtualvoid add(component*) {}
};
class Composite: public component{
vectorc;
public:
composite(intval): component(val) {}
voidadd(component* elem) {
c.puch_back(elem);
}
…
};
prototype
我要创建未来
LandSatImage是一个子类,都有一个静态的自己,很久以前写好的框架要能看得到后来写的东西,-LandSatImage(),负的代表private,#代表Protected,我们故意把构造函数设为私有,那在创建的时候私有的构造函数可以被调用起来吗?可以,因为是自己人,不是外界在调用,我们就借用这个私有的构造函数,addPrototype(this).而且挂上去在父类中看得到。父类Image中,有addPrototype(p:Image*):void而且所有的子类都应该有一个函数clone():Image*,做的事情是return
new LandSatImage.通过原型这个对象可以调用clone这个函数,如果没有这个原型的话。
D.复合,继承关系下的构造和析构
继承关系下的构造和析构
复合关系下的构造和析构
继承加上复合关系下的构造和析构
我们要谈的是class和class之间的关系,三种关系。当发生继承时,子类对象里头有一个父类的成分。猪是一种哺乳动物,猪里头就应该有哺乳动物的成分,哺乳动物应该有动物的成分。由于有这样的关系,当我们在探讨构造和析构的时候,我们要注意由内而外,析构函数要先执行自己,再调用父类的析构。
继承和复合关系下的构造和析构,子类的构造函数首先调用父类的default构造函数,然后调用component的default构造函数,最后才执行自己。
析构由外而内,子类的析构函数首先执行自己,然后调用component的析构函数,最后调用base的析构函数。
本次作业要求构造十个随机矩形和随机十个圆并根据他们的面积进行删选,遇到的问题有一开始思考如何将两个类的对象放在一个数组里,很普通的问题,说明肯定已经有完美的解决方案,多态的使用,即使用基类指针指向子类对象,这时一定要注意要在子类中使用的方法一般都要在基类中使用虚函数或者纯虚函数先写出来,再在下面的子类中进行重载。包含有纯虚函数的类叫做虚类,那什么时候我们需要构建一个虚类呢?当类是一个抽象概念的时候,比如这个题目中形状这个类,我们就要构建它为虚函数,因为没有一个对象是一个形状,我们也不会将这个类实例化。具体的做法是shape** a = new shape* [20],这样就构建了一个指针数组,且数组也使用指针表示,第二个遇到的问题是在删选出合适的对象后,我一开始想要调用拷贝赋值函数,发现很麻烦,实际上可以直接拷贝指针,省去很多问题。然后不需要建立两个数组,可以用快慢指针的方法将符合要求的一个个拷贝到当前数组,并将不符合的删除,注意在删除的时候要将指针置为空指针,在删除数组的时候要用delete[]。