自生成方法
众所周知,C++类有四种函数如果不显示定义会自动生成:构造函数、析构函数、拷贝构造函数、运算符重载(operator = )。在继承时,很多特殊情况也就出在这四类函数中。
继承中派生类内包含了基类内所有方法和属性,只是由于private 权限修饰不能在派生类中直接调用而已。但是基类构造函数明明就算设置成public,在子类对象中也是不能直接使用的。这其实就很容易理解:基类和派生类的构造、析构、拷贝构造甚至都不重名,运算符重载参数也一般直接指定的本对象的类型(如果想直接使用父类的运算符重载函数,不在子类中显示声明,编译会报错)。
#include <string>
class CMyString : public std::string
{
public:
CMyString( void ){};
virtual ~CMyString( void ){};
/** 继承基类的=运算符 */
using std::string::operator=;
};
void main()
{
CMyString strMine;
strMine = "我继承了等号运算符~";
}
所以一般的使用范例是直接在派生类的相关函数中显示的调用父类函数
class Base{
public:
virtual ~Base();
Base(const char *pStr);
Base(const Base &other);
virtual void CallFunction() ;
public:
char *m_pBase;
};
Base::Base(const char *pStr){
if (pStr) {
long iLen = strlen(pStr)+1;
m_pBase = new char[iLen];
memset(m_pBase, 0, iLen);
strcpy(m_pBase, pStr);
}}
Base::~Base(){
if (m_pBase) {
delete [] m_pBase;
m_pBase = NULL;
}
}
Base::Base(const Base &other){
if (m_pBase) {
delete m_pBase;
m_pBase = NULL;
}
long iLen = strlen(other.m_pBase)+1;
m_pBase = new char[iLen];
memset(m_pBase, 0, iLen);
strcpy(m_pBase, other.m_pBase);
}
class Child:public Base{
public:
~Child();
Child(const char *pStr , const char *pBase);
Child(const Child &other);
public:
char *m_pChild;
};
Child::Child(const char *pStr , const char *pBase):Base(pBase){//初始化列表中调父类构造函数
if (pStr) {
long iLen = strlen(pStr)+1;
m_pChild = new char[iLen];
memset(m_pChild, 0, iLen);
strcpy(m_pChild, pStr);
}
}
Child::Child(const Child &other):Base(other){//调父类拷贝构造函数
if (m_pChild) {
delete m_pChild;
m_pChild = NULL;
}
long iLen = strlen(other.m_pChild)+1;
m_pChild = new char[iLen];
memset(m_pChild, 0, iLen);
strcpy(m_pChild, other.m_pChild);
}
Child::~Child(){
if (m_pChild) {
delete [] m_pChild;
m_pChild = NULL;
}
}
其他方法
而对于一般成员方法来说,值得注意的就是类内的重载,派生类的重写,还有方法重名时的隐藏。重载是指同类内同名方法不同参数(注意和返回值无关)。
重写是指有父子关系的两个类中同名方法相同参数,甚至返回值都要相同。重写的场景发生在父类的方法不再适用用子类,子类中使用同名方法重新实现一次(当然可以使用 ‘:’先运行一次父类的函数),是方便子类创建对象后运行不同于父类的有自己特性的方法,如果重写的函数在父类中添加了virtual,则是方便多态:即方便用父类指针调用子类对象有自己特性的方法。
覆盖是指有父子关系的两个类中同名方法。比如基类中有三个funcName(…)函数,只要子类中定义了同名的(只要名字相同)funcName()函数,则子类对象中所有父类的funcName函数都无法访问了。
办法1:在派生类里覆盖所有基类的的fcn方法,在方法体里,直接调用Base::funcName,但是这样太麻烦了,每一个都要覆盖。
办法2:使用using。只需要在派生类的public下,using Base::funcName; ,就达到了办法1的效果。
class D1 : public Base{
public:
using Base::fcn;//使用using后,就把Base里的,所有fcn为名字的成员方法,都引入到D1里面,所以就可以使用D1的对象来调用Base里的任何fcn为名字的成员方法了。
int fcn(){
std::cout << "D1 fcn()" << std::endl;
}
};
使用接口
C++的多继承带来有名的菱形继承问题,如果如果能使用纯虚函数,纯虚类。将通过将构造函数的权限设置为protected达到模拟java接口的目的。在初次实现时尽量将has-a的方法抽象出去变成接口,可以有效的降低系统中出现难以溯源的继承问题。