11, 组合和继承


一, Composition 复合  has-a的关系

简单来讲, 就是:
class A{
    classB b1;  
};

这里讲到Adapter设计模式:
template<class T>
class queue{
protected:
    deque<T> c;
......
};

queue为 最先插入在元素将是最先被删除;反之最后插入的元素将最后被删除,因此队列又称为“先进先出”(FIFO—first in first out)的线性表。
deque为 双端队列中的元素可以从两端插入也可以从两端弹出。
所以 queue只用到了deque的一部分内容,即对deque进行了封装,将功能强大的类封装为特性化的类。-Adapter

构造析构: 
构造:由内到外
析构:由外到内



二,Delegation 委托      Composition by reference( 这里其实包含指针和引用两种情况 )


如:
class StringRap;
class String {

.......
private:
    StringRap* rep;

};
委托: 我拥有你, 但我只在想用你的时候才去实现你.(指向对象的指针)
class StringRap{
friend class String;
.....
};

以上的String实际实现是由StringRap类来操作. 这里实际上运用了一个设计模式:
Handle/Bady - pImpl
其中在String类中的StringRap指针就是Handle, StringRap类就是Bady.
在String类在进行相互赋值的时候, 其实底层都是StringRap实现的, 
StringRap拷贝实现其实是, 把当前对象指向的地址返给新的对象, 然后自己reference counting++ 
而当其中一个String值发生改变的时候, 那么就会申请一块新空间,并赋值为当前的内容,并单独分给要改变的Stirng. 
真是妙哉!


三, 继承 Is-a

分public, protected, private 
构造:由内到外
析构:由外到内 
注意:  很多时候要求父类的析构是virtual 




12, 虚函数与多态

继承的是调用权而非功能 - 关键句





非虚: 不希望子类override

虚: 希望子类override , 但自己已有定义

纯虚: 子类一定要定义它





虚函数重要用途: 

    子类对象调用父类非虚函数funA(), 而funA()中调用了虚函数funB(), 而子类override了funB(), 那么funA中将调用子类的funB()而不是父类的funB.

即funA()中的关键功能推迟实现 - Template Method

这里的funA称之为应用框架. 





那么这里的子类funB是如何被调用起来的?

答: 

class CMyDoc: public CDocument 

myDoc.Open() ->CDocument::Open(&this)  

这里显示出了this指针是多么的神奇



13, 委托相关设计


委托+继承

Composite设计模式 较好的说明了 继承+委托 的相互作用关系. 
例子: 









如上视图中, 在一个文件夹中有文件夹也有文件, 而文件夹中的文件夹很可能也有文件夹和文件....

这里就需要一个类创建的对象中,既能保存一个文件夹,也能保存一个文件;而其中的文件夹还能保存文件及文件夹. 





所以, 用到委托+继承来解决这个问题:





设: 

class Primitive代表文件

class Composite代表文件夹 ,文件夹中肯定是可以放文件夹和文件的.

所以我们在类Composite中有 vector<Composite*> 和vector <Primitive*>

那当然也有函数 add(Composite )和add(Primitive)分别用来保存文件夹中含有的文件夹和文件.

有没有发现很麻烦? 如果我们能够只用一个vector和一个add是不是会更省事?





此时,我们使用 继承





创建基类: 

class Component

{

//..

    public:

    virtrual void add(Component*) { }        //这里不能为纯虚函数 . 你猜 , 为什么? 

};





而另外两个类应该继承Component:





class Primitive : public Component

{

//..

};





class Composite : public Component

{

    vector<Compenent*> c;

public:

       void add(Component* elem){

        c.push_back(elem);

    }

//....

};













继承+委托

Prototype设计模式

让父类管理未来的子类,在父类还不知道子类类名的时候就可以进行管理了. 这里的管理指, 所有从本父类派生出去的子类对象都能够通过父类名称来得到.





如果要通过父类名称来获得子类的对象, 那么我们就需要:

    static Image *_prototypes[10];

    static int _nextSlot;

这样就可以用于保存子类的对象. 别忘了在类声明外部,定义这些静态变量哟! ~ 





那我们如何将子类的对象保存到这个数组中? 那我们需要子类调用一个函数,把自己给放进来:

static void addPrototype(Image *image)

    {

        _prototypes[_nextSlot++] = image;

    }





子类如何主动触发去调用addPrototype这个函数呢? 我们的目的就是不让用户根据子类名称去创建对象,而是通过父类.

所以前提条件就是:

子类默认构造函数为私有:

 private:

    LandSatImage()

    {

        addPrototype(this);

    }

那如何创建这个"this"对象呢? 那么我就应该在子类内声明一个本类的成员变量,并且为静态:

    // This is only called when the private static data member is inited

    static LandSatImage _landSatImage;

这样在程序运行起来的时候就会创建.

别忘了在类外定义该静态对象:

// Register the subclass's prototype

LandSatImage LandSatImage::_landSatImage;

此时就会调用构造函数, 并且将自己放到父类的静态数组中. 

到此我们已经在父类中拥有了这个LandSatImage子类的对象.如果有多个不同子类那么就会加进来更过不同的子类对象.

那如何通过父类来获得特定子类的一个对象?而且我们是不知道子类的名称的, 所以这个创建新子类对象的实现办法只能让子类自己去完成:

在子类中:

 Image *clone()

    {

        return new LandSatImage(1);    //new有没有什么问题?为什么是1

    }





在父类中如何才能得到该特定的子类呢? 父类中的实现办法:

Image *Image::findAndClone(imageType type)

{

  for (int i = 0; i < _nextSlot; i++)

    if (_prototypes[i]->returnType() == type)

      return _prototypes[i]->clone();



此时的type就是用户自己设定的子类类型. 是子类编写用户去添加的枚举类型, 父类只负责判断是否相等就行,这样就将需要判断的类型定义也推迟到后期实现.

枚举内加入新子类的类型:

enum imageType

{

  LSAT, SPOT

};









其实上的子类clone中的new是有玄机的. 你想想 如果 new又调用了默认构造函数的话,又把自己这种类型的子类addPrototype到父类的静态数组中,这就不能保证数组中单一子类型的唯一性了, 所以我们需要写一个新的构造函数:

  protected:

    // This is only called from clone()

    LandSatImage(int dummy)

    {

        _id = _count++;

    }

这里为了和默认构造函数区分开,我们多个int参数.int并没有用,所以在new LandSatImage(1)的1是随便写的.




完整代码:
#include <iostream.h> //
enum imageType
{
LSAT, SPOT
}; class Image
{
public:
virtual void draw() = 0;
static Image *findAndClone(imageType);
protected:
virtual imageType returnType() = 0;
virtual Image *clone() = 0;
// As each subclass of Image is declared, it registers its prototype
static void addPrototype(Image *image)
{
_prototypes[_nextSlot++] = image;
}
private:
// addPrototype() saves each registered prototype here
static Image *_prototypes[10];
static int _nextSlot;
}; Image *Image::_prototypes[];
int Image::_nextSlot; // Client calls this public static member function when it needs an instance
// of an Image subclass
Image *Image::findAndClone(imageType type)
{
for (int i = 0; i < _nextSlot; i++)
if (_prototypes[i]->returnType() == type)
return _prototypes[i]->clone();
} class LandSatImage: public Image
{
public:
imageType returnType()
{
return LSAT;
}
void draw()
{
cout << "LandSatImage::draw " << _id << endl;
}
// When clone() is called, call the one-argument ctor with a dummy arg
Image *clone()
{
return new LandSatImage(1);
}
protected:
// This is only called from clone()
LandSatImage(int dummy)
{
_id = _count++;
}
private:
// Mechanism for initializing an Image subclass - this causes the
// default ctor to be called, which registers the subclass's prototype
static LandSatImage _landSatImage;
// This is only called when the private static data member is inited
LandSatImage()
{
addPrototype(this);
}
// Nominal "state" per instance mechanism
int _id;
static int _count;
}; // Register the subclass's prototype
LandSatImage LandSatImage::_landSatImage;
// Initialize the "state" per instance mechanism
int LandSatImage::_count = 1; class SpotImage: public Image
{
public:
imageType returnType()
{
return SPOT;
}
void draw()
{
cout << "SpotImage::draw " << _id << endl;
}
Image *clone()
{
return new SpotImage(1);
}
protected:
SpotImage(int dummy)
{
_id = _count++;
}
private:
SpotImage()
{
addPrototype(this);
}
static SpotImage _spotImage;
int _id;
static int _count;
}; SpotImage SpotImage::_spotImage;
int SpotImage::_count = 1; // Simulated stream of creation requests
const int NUM_IMAGES = 8;
imageType input[NUM_IMAGES] =
{
LSAT, LSAT, LSAT, SPOT, LSAT, SPOT, SPOT, LSAT
}; int main()
{
Image *images[NUM_IMAGES]; // Given an image type, find the right prototype, and return a clone
for (int i = 0; i < NUM_IMAGES; i++)
images[i] = Image::findAndClone(input[i]); // Demonstrate that correct image objects have been cloned
for (i = 0; i < NUM_IMAGES; i++)
images[i]->draw(); // Free the dynamic memory
for (i = 0; i < NUM_IMAGES; i++)
delete images[i];
}

彩蛋:

C++面向对象高级编程(下)-Geekband的更多相关文章

  1. C++面向对象高级编程(上)-Geekband

    头文件和类声明 一定要注意使用防卫式的头文件声明: #ifndef _CLASSHEAD_ #define _CLASSHEAD_ . . . . #endif 基于对象和面向对象 : 基于对象 单一 ...

  2. C++面向对象高级编程(五)类与类之间的关系

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 本节主要介绍一下类与类之间的关系,也就是面向对象编程先介绍两个术语 Object Oriented Programming   OOP面向对象编 ...

  3. C++面向对象高级编程(三)基础篇

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 概要 一.拷贝构造 二.拷贝赋值 三.重写操作符 四.生命周期 本节主要介绍 Big Three 即析构函数,拷贝构造函数,赋值拷贝函数,前面主 ...

  4. C++面向对象高级编程(一)基础篇

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 概要: 知识点1 构造函数与析构函数 知识点2 参数与返回值 知识点3 const 知识点4 函数重载(要与重写区分开) 知识点5 友元 先以C ...

  5. Java面向对象 网络编程 下

    Java面向对象 网络编程  下 知识概要:                   (1)Tcp 练习 (2)客户端向服务端上传一个图片. (3) 请求登陆 (4)url 需求:上传图片. 客户端:   ...

  6. C++面向对象高级编程(九)Reference与重载operator new和operator delete

    摘要: 技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一 Reference 引用:之前提及过,他的主要作用就是取别名,与指针很相似,实现也是基于指针. 1.引用必须有初值,且不能引用nul ...

  7. C++面向对象高级编程(八)模板

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 这节课主要讲模板的使用,之前我们谈到过函数模板与类模板 (C++面向对象高级编程(四)基础篇)这里不再说明 1.成员模板 成员模板:参数为tem ...

  8. C++面向对象高级编程(七)point-like classes和function-like classes

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 1.pointer-like class 类设计成指针那样,可以当做指针来用,指针有两个常用操作符(*和->),所以我们必须重载这两个操作 ...

  9. C++面向对象高级编程(六)转换函数与non-explicit one argument ctor

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 1.conversion function 转换函数 //1.转换函数 //conversion function //只要你认为合理 你可以任 ...

  10. C++面向对象高级编程(四)基础篇

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一.Static 二.模板类和模板函数 三.namespace 一.Static 静态成员是“类级别”的,也就是它和类的地位等同,而普通成员是“ ...

随机推荐

  1. Allegro文档错误之:Iangle 命令

    Allegro绘制弧线时,可以使用add rarc命令,或者菜单栏里 Add|Arc w/Radius. 使用该命令时,需要输入3个参数: 1,圆心坐标:如 x –0.3 –0.8 2,半径,以及起始 ...

  2. npm与cnpm两者之间的区别是什么?

    NPM(节点包管理器)是节点的包管理器,用于管理节点插件(包括安装.卸载和管理依赖关系等).).NPM是一个软件包管理工具,安装在新版本的节点上,所以我们需要安装节点. NPM的常用场景(http:/ ...

  3. 配置文件一spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  4. 应用上云新模式,Aliware 全家桶亮相杭州云栖大会

    全面上云带来的变化,不仅是上云企业数量上的攀升,也是企业对云的使用方式的转变,越来越多的企业用户不仅将云作为一种弹性资源,更是开始在云上部署架构和应用,借助 Serverless 等技术,开发人员只需 ...

  5. lxhgww的奇思妙想 长链剖分板子

    https://vijos.org/d/Bashu_OIers/p/5a79a3e1d3d8a103be7e2b81 求k级祖先,预处理nlogn,查询o1 //#pragma GCC optimiz ...

  6. jenkins实现不同角色查看不同视图

    1.安装插件Role-based Authorization Strategy 2.开启插件 系统管理>>>全局安全配置 3.创建角色和用户 4.登陆查看,只能看到travel开头的 ...

  7. WinDBG常用断点命令

    WinDBG提供了多种设断点的命令: bp 命令是在某个地址 下断点, 可以 bp 0x7783FEB 也可以 bp MyApp!SomeFunction . 对于后者,WinDBG 会自动找到MyA ...

  8. 解决asp.net web api时间datetime自动带上带上的T和毫秒的问题

    今天用asp.net web api写微信小程序的接口时遇到一个问题. 返回的model中的datetime类型的字段自动转换成了“2014-11-08T01:50:06:234”这样的字符串,带上的 ...

  9. 2019-8-31-dotnet-获取指定进程的输入命令行

    title author date CreateTime categories dotnet 获取指定进程的输入命令行 lindexi 2019-08-31 16:55:58 +0800 2019-0 ...

  10. 【默默努力】PixelFire

    先放下我玩游戏的效果图: 关于游戏最后的结束部分其实我还没有截图,看着挺好看的,后面的效果 再放作者大大的项目地址:https://github.com/panruiplay/PixelFire 接下 ...