OOP: Object Oriented Programming (面向对象编程)
OOD: Object Oriented Design (面向对象设计)
C++中类与类之间有什么关系呢?
- Inheritance (继承,表现为is-a)
- Compositon (复合,表现为has-a)
- Delegation (委托,表现为is-a + pointer)
下面将逐一探讨三个关系以及它们的组合
1. Composition
表示类之间的has-a关系,即class A中有class B
template <class T>
class queue{
...
protected:
deque<T> c; //底层容器
public:
//以下完全利用c的操作函数完成
bool empty() const {return c.empty(); }
size_type size() const {return c.size(); }
reference front() {return c.front(); }
reference back() {return c.back(); }
void push(const value_type& x){ c.push_back(x);}
void pop() {c.pop_front(); }
}
如上面代码中所显示的,deque c本身就是一个类,而这个类又被包含在了我们定义的新类queue中。注意: 包含可以是不完整的,即class B可以是部分被包含进class A。例如,B有function_1, function_2, function_3三个函数,而A只需要使用其中一个函数,那么A只需要在自己的类中定义一个成员函数去调用B的对应函数即可。
- 从内存的角度上来看,对于复合关系,class A复合class B, 复合后内存也会相加;
- Container(即class A)的构造函数首先调用Component(即class B)的default构造函数,然后才执行自己。(构造由内而外)
Container::Container(...): Component() {...};
- Container(即class A)的析构函数首先析构自己,然后才调用Component的析构函数。(析构由外而内)
Container::~Container(...) ~Component() {...};
2. Delegation
表示委托,某种程度上可以理解成复合的特殊形式 Composition by reference, 但是包含在其中的class B是以指针的形式被复合的 (经典评论: 委托就是外包)
如果我有一个指针,通过指针访问了另外一个类,这个有,有点虚,只是有一个指针指向了它。
class StringRep;
class String {
public:
String();
String(const char* s);
String(const String& s);
String &operator=(const String& s);
~String();
private:
StringRep* rep; // pImlp: Pointer Implementary
};
Delegation和Composition的区别在于拥有组件的声明周期不一样
- class A以Delegation的形式和class B关联,那么我可以保持class A不变,而class B可以任意发生变化,因为它们两者中间的媒介是指针,甚至我可以通过继承B去定义新的子类class C,这时借助多态等性质,还能通过class A去调用class C。
- 我们可以把class A想象成客户端、class B想象成服务器端,那么这个时候,Delegation机制就可以理解成编译防火墙。
3. Inheritance
继承可表示为is-a,联系生物界的界门纲目科属种,就是这样的一个模式
在C++中,有public继承、private继承以及projected继承
struct _List_node_base
{
_List_node_base* _M_next;
_List_node_base* _M_prev;
};
template<typename _Tp>
struct _List_node: public _List_node_base
{
_Tp _M_data;
};
我们要明白,继承的东西是什么?应该注意,子类从父类继承了数据,同时还有函数的调用权
-
从内存的角度看:子类的对象中含有父类的成分,这一点与复合关系是相同的
-
同样地,构造过程由内而外,析构由外而内
- 子类的构造函数首先构造父类的默认构造函数,然后才执行自己
- 子类的析构函数首先析构自己,然后才调用父类的析构函数
- 父类的析构函数必须是virtual, 否则会出现undefined behavior
-
继承最优价值的地方在于它和虚函数的搭配
- 虚函数:在任意函数名前加上virtual,该函数就变为虚函数
| 关键字 | 性质 |
|---|---|
| non-virtual | 你不希望派生类重新定义的函数(override,覆写) |
| virtual | 你希望派生类去重新定义它(override), 并且你已经对它有定义 |
| pure virtual | 纯虚函数,你希望派生类一定要重新定义它,并且在父类中你对它没有定义, 纯虚函数一定要被子类实现 |
class Shape{
public:
virtual void draw() const=0; // pure virtual
virtual void error(const std::string& msg) // impure-virtual
int objectID() const; // non-virtual
}
class Rectangle: public Shape {...}
class Ellipse: public Shape {...}
设计模式中有一种template method即借助继承+虚函数来实现。
template method是指通过延缓某些函数或者关键步骤,并将其交由子类来实现。
4. Inheritance + Composition
继承+委托是功能最强大的
class Subject
{
int m_value;
vector<Ovserver*>m_views;
public:
void attach(Observer* obs)
{
m_views.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);
}
};
class Observer
{
public:
virtual void update(Subject* sub, int value)=0; //通知内容发生了改变
};
不同的类可以继承Observer类,做到统一访问Subject,可以理解成从不同角度看同一个东西
观察者模式!!!
23个设计模式
继承、复合与委托 Big Three
又要添加左边,又要添加右边,那就给左边和右边找一个父类,然后添加父类的指针
这也是委托+继承双重实现的效果
class Component
{
int value;
public:
Component(int val) {value=val};
virtual void add(Component*){}
};
class Primitive: public Component
{
public:
Primitive(int val): Component(val){}
}
Prototype(原型模式)
问题: 我现在要去创建未来的class对象,但是我甚至不知道它的名字叫什么
解法: 子类自己去创造自己,然后在私有构造函数中 把自己add Prototype到父类中,然后通过原型的clone方法去生成新的对象,相当于我在初始化时送一个样本到父类,然后父类就可以根据这个样本去生成其他的样本了
Design Pattern Explained Simply
#include<iostream.h>
enum imageType
{
LSAT,SPOT
};
class Image
{
public:
virtual void draw()=0;
}
本文详细探讨了C++中面向对象的三种关系:继承(is-a)、复合(has-a)和委托。通过实例解释了每种关系的概念、内存管理以及在设计模式中的应用,特别是继承与虚函数的结合。最后提到了继承和复合的组合在观察者模式中的应用以及原型模式解决的问题。
134

被折叠的 条评论
为什么被折叠?



