一.问题背景
最近在看基类指针转换成派生类指针的问题,看到一个帖子讨论的挺有意思(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构造函数的区别,一个是有参构造函数,把外边的指针传过去;另一个是无参构造,在构造函数里面去创建指针.