C++面向对象高级编程(下)-Geekband



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];
}





彩蛋:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值