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


    彩蛋:
  • 相关阅读:
    “Win10 UAP 开发系列”之 在MVVM模式中控制ListView滚动位置
    “Win10 UAP 开发系列”之主题模式切换
    Windows Phone 8.1中AppBarToggleButton的绑定问题
    Windows Phone 8.1中处理后退键的HardwareButtons.BackPressed事件
    在后台代码中动态生成pivot项并设置EventTrigger和Action的绑定
    数据对象转json与md5加密注意事项
    iOS中wkwebview加载本地html的要点
    iOS项目开发常用功能静态库
    AFN中请求序列化的设置
    swift中的AnyHashable
  • 原文地址:https://www.cnblogs.com/skyhuangdan/p/5486766.html
Copyright © 2020-2023  润新知