从基类指针转换成派生类指针到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构造函数的区别,一个是有参构造函数,把外边的指针传过去;另一个是无参构造,在构造函数里面去创建指针.

 

posted @ 2020-05-08 16:35  心媛意码  阅读(512)  评论(0编辑  收藏  举报