zoukankan      html  css  js  c++  java
  • 从基类指针转换成派生类指针到Is A和Has A的使用

    一.问题背景

    最近在看基类指针转换成派生类指针的问题,看到一个帖子讨论的挺有意思(https://bbs.csdn.net/topics/330009840).今天花时间看了下.发现有了一些收获,不过也存在一些困惑,现记录在这里,以便以后能够有据可查.

    问题大概是这样,楼主想要继承第三方库中的类,在派生类中增加新的方法.在这个过程中没有修改原类,楼主想要通过将基类指针转为派生类指针的方法来实现调用新方法.但他看到有论述说"不能够将基类指针转化为派生类指针(实际上如果此时的基类指向派生类对象的话,可以发生这种转换,我的批注)",故在论坛里对这一问题进行了交流.(我存在的疑惑:既然他可以使用子类来调用新方法,干嘛还要使用基类呢?)

    当时的代码如下:

    (这里实际上实现的功能是修改m_passed,注意尽管基类中存在有参构造函数,但他只能在初始化的时候指定m_passed的值,而不能在过程中去修改它的值.)

    class BaseClass
    {
    public:
            int getPassed() { return m_passed; }
            BaseClass (int passsed):m_passed(passsed) {}
    protected:
            int m_passed;
    };
    
    class ChildBaseClass: public BaseClass
    {
    public:
            ChildBaseClass():BaseClass(0) {}
            void setPassed(int passed) {m_passed = passed;}
    };
    
    int main(int argc, char *argv[])
    {
            ChildBaseClass child;
            cout << child.getPassed() << endl;
            child.setPassed(1);
            cout << child.getPassed() << endl;
    
            BaseClass base(0);
            cout << base.getPassed() << endl;
            ChildBaseClass *pchild = (ChildBaseClass *) &base;
            pchild->setPassed(1);
            cout << base.getPassed() << endl;
            return 0;
    }

    2.问题探讨:

    1.关于这个问题,我在<C++ Primer>习题中见到过相关的论述:

    这里的意思是,如果只是添加新的成员函数,可以通过基类转换为派生类的方式来访问.(不过需要用dynamic_cast来保证安全性)

    我估计大概的使用套路是这样的:

    if(Derived *d = dynamic_cast<Derived *>(p))
    {
        d->fun();
    }
    else
    {
        cerr << "调用子类新方法时出错" << endl;
    }

     修改后的程序如下所示: 

    注意:

    1.dynamic_cast使用时,需要保证基类存在虚函数

    2.dynamic_cast转换成功需要保证基类指针指向子类对象

    class BaseClass
    {
    public:
        virtual void vf()    //dynamic_cast使用时,需要保证基类存在虚函数
        {
        }
        int getPassed() { return m_passed; }
        BaseClass(int passsed) :m_passed(passsed) {}
    protected:
        int m_passed;
    };
    
    class ChildBaseClass : public BaseClass
    {
    public:
        ChildBaseClass() :BaseClass(0) {}
        void setPassed(int passed) { m_passed = passed; }
    };
    
    int main(int argc, char *argv[])
    {
        ChildBaseClass child;
        cout << child.getPassed() << endl;
        child.setPassed(1);
        cout << child.getPassed() << endl;
    
        BaseClass *pb = new ChildBaseClass();    
        cout << pb->getPassed() << endl;
        if (ChildBaseClass *pchild = dynamic_cast<ChildBaseClass *> (pb))
        {
            pchild->setPassed(1);
        }
        else
        {
            cerr << "dynamic_cast转换失败" << endl;
        }
        cout << pb->getPassed() << endl;
        return 0;

    2.论坛中"arong1234"认为楼主的那种方式存在隐患,如果以后子类中添加了成员变量的话,可能会导致问题;再者应该用has-a的模式去扩展类,去代替is-a的模式.

    这种模式的特点是将底层库的指针作为扩展类的成员变量,通过这个指针来使用底层库的方法.

    楼主的实现:

    class BaseClass
    {
    public:
        int getPassed() { return m_passed; }
        BaseClass(int passsed) :m_passed(passsed) {}
    protected:
        int m_passed;
    };
    
    class WrapperBaseClass
    {
    public:
        WrapperBaseClass(BaseClass * pBase):m_pBase(pBase) //这种方式是外面把BaseClass指针传进来,给到m_pBase
        {
        }
    
        void setPassed(int passed)
        {
            BaseClass  *pBase = new BaseClass(passed);
            *m_pBase = *pBase;
            delete pBase;
        }
    
        BaseClass& getBase() { return *m_pBase; }
        BaseClass* get() { return m_pBase; }
    
    private:
        WrapperBaseClass(const WrapperBaseClass& other) {/* 不允许复制 */ }
    
    protected:
        BaseClass *m_pBase;
    };
    
    int main(int argc, char *argv[])
    {
        BaseClass base(0);
        cout << base.getPassed() << endl;
        WrapperBaseClass wrap(&base);    //注意创建对象的写法和创建指针时的写法.
        WrapperBaseClass *pWrap = new WrapperBaseClass(&base);
        wrap.setPassed(1);
        cout << base.getPassed() << endl;
        cout << wrap.getBase().getPassed() << endl;
        return 0;
    }

    自己想到的方式:

    class BaseClass
    {
    public:
        BaseClass()
        {
            m_passed = 0;
        }
    
        int &getPassed()
        {
            return m_passed;
        }
    
        BaseClass(int passsed) :m_passed(passsed)//有参构造函数
        {
        }
    private:
        int m_passed;
    };
    
    //Wrapper类实现方式1
    class BaseClassWrapper
    {
    public:
        BaseClassWrapper()
        {
            m_pBase = new BaseClass();    //这里是自己在构造函数中创建对象
        }
        ~BaseClassWrapper()
        {
            if (m_pBase)
            {
                delete m_pBase;
                m_pBase = NULL;
            }
        }
        BaseClass *GetBasePtr()
        {
            return m_pBase;
        }
    
        BaseClass GetBaseObj()
        {
            return *m_pBase;
        }
    
    private:
        BaseClass *m_pBase;
    
    public:
        void SetPassed(int passed)    //作用:修改基类中的m_passed的值.
        {
            BaseClass *pb = new BaseClass(passed);
            *m_pBase = *pb;    //对象的赋值操作.不能够用指针赋值,因为下面马上要delete掉pb了.
            delete pb;
        }
    };
    
    //Wrapper类实现方式2
    //class BaseClassWrapper
    //{
    //public:
    //    BaseClassWrapper()
    //    {
    //        m_pBase = new BaseClass();
    //    }
    //    ~BaseClassWrapper()
    //    {
    //        if (m_pBase)
    //        {
    //            delete m_pBase;
    //            m_pBase = NULL;
    //        }
    //    }
    //    BaseClass *GetBasePtr()
    //    {
    //        return m_pBase;
    //    }
    //
    //    BaseClass GetBaseObj()
    //    {
    //        return *m_pBase;
    //    }
    //private:
    //    BaseClass *m_pBase;
    //
    //public:
    //    void SetPassed(int passed)    //封装好的接口,通过成员变量m_pBase来修改基类中的m_passed的值.
    //    {
    //     m_pBase->getPassed() = passed; //注意这里的getPassed()需要为引用类型,否则它就是getPassed()就是右值.
    //    }
    //};
    
    int main()
    {
        BaseClassWrapper bcw;
        cout << "初始化时m_passed的值" << bcw.GetBasePtr()->getPassed() << endl;
        bcw.SetPassed(1);
        cout << "修改后m_passed的值" << bcw.GetBasePtr()->getPassed() << endl;
        return 0;
    } 

    对比楼主和我在BaseClassWrapper构造函数的区别,一个是有参构造函数,把外边的指针传过去;另一个是无参构造,在构造函数里面去创建指针.

    新战场:https://blog.csdn.net/Stephen___Qin
  • 相关阅读:
    解决IE下a标签点击有虚线边框的问题
    解决IE8下opacity属性失效问题
    用Vue.js开发微信小程序:开源框架mpvue解析
    使用pie.htc时Border-radius的兼容
    解决IE8下CSS3选择器 :nth-child() 不兼容的问题
    jQuery兼容浏览器IE8方法
    css3兼容IE8的方案 各个ie的hack
    JavaScript之旅(DOM)
    JavaScript之旅(三)
    JavaScript之旅(二)
  • 原文地址:https://www.cnblogs.com/Stephen-Qin/p/12846733.html
Copyright © 2011-2022 走看看