基类的同名成员在派生类中被屏蔽,成为不可见的,或者说,派生类新增加的同名成员覆盖了基类中的同名成员。因此如果在定义派生类对象的模块中通过对象名访问同名的成员,则访问的是派生类的成员。请注意:不同的成员函数,只有在函数名和参数个数相同、类型相匹配的情况下才发生同名覆盖,如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载。
多态满足条件:
1、有继承关系
2、子类重写父类中的虚函数 (重写:函数返回值类型 函数名 参数列表 完全相同)
多态使用:
父类指针或引用指向子类对象
重写/覆盖:函数返回值类型 函数名 参数列表(参数的个数和类型) 完全相同。如果不同,就成为函数的重载而不是覆盖了。
虚函数:就是在基类声明函数是虚拟的,并不是实际存在的函数,然后在派生类中才正式定义此函数。在程序运行期间,用指针指向某一派生类对象,这样就能调用指针指向的派生类对象中的函数,而不会调用其他派生类中的函数。
class ClassA
{
public:
ClassA() { cout << "ClassA::ClassA()" << endl; }
virtual ~ClassA() { cout << "ClassA::~ClassA()" << endl; }
void func1() { cout << "ClassA::func1()" << endl; }
void func2() { cout << "ClassA::func2()" << endl; }
virtual void vfunc1() { cout << "ClassA::vfunc1()" << endl; }
virtual void vfunc2() { cout << "ClassA::vfunc2()" << endl; }
private:
int aData;
};
class ClassB : public ClassA
{
public:
ClassB() { cout << "ClassB::ClassB()" << endl; }
virtual ~ClassB() { cout << "ClassB::~ClassB()" << endl; }
void func1() { cout << "ClassB::func1()" << endl; }
virtual void vfunc1() { cout << "ClassB::vfunc1()" << endl; }
private:
int bData;
};
class ClassC : public ClassB
{
public:
ClassC() { cout << "ClassC::ClassC()" << endl; }
virtual ~ClassC() { cout << "ClassC::~ClassC()" << endl; }
void func2() { cout << "ClassC::func2()" << endl; }
virtual void vfunc2() { cout << "ClassC::vfunc2()" << endl; }
private:
int cData;
};
三个类中的 func1 和 func2 函数,他们不是虚函数,彼此互不关联,都是各自独立的,不存在重载一说,在调用的时候也不需要进行查表的操作,直接调用即可。
有时在基类中定义的非虚函数会在派生类中被重新定义,如果用基类指针调用该成员函数,则系统会对象中基类部分的成员函数;如果用派生类指针调用该成员函数,则系统会调用派生类对象中的成员函数。这并不是多态性行为,没有用到虚函数的功能。
ClassB继承与ClassA,其虚函数表是在ClassA虚函数表的基础上有所改动的,变化的仅仅是在子类中重写的虚函数。如果子类没有重写任何父类虚函数,那么子类的虚函数表和父类的虚函数表在内容上是一致的。
ClassA *a = new ClassB();
a->func1(); // "ClassA::func1()" 隐藏了ClassB的func1()
a->func2(); // "ClassA::func2()"
a->vfunc1(); // "ClassB::vfunc1()" 重写了ClassA的vfunc1()
a->vfunc2(); // "ClassA::vfunc2()"
ClassA* a = new ClassC;
a->func1(); // "ClassA::func1()" 隐藏ClassB::func1()
a->func2(); // "ClassA::func2()" 隐藏ClassC::func2()
a->vfunc1(); // "ClassB::vfunc1()" ClassB把ClassA::vfunc1()覆盖了
a->vfunc2(); // "ClassC::vfunc2()" ClassC把ClassA::vfunc2()覆盖了
ClassB* b = new ClassC;
b->func1(); // "ClassB::func1()" 有权限操作时,子类优先
b->func2(); // "ClassA::func2()" 隐藏ClassC::func2()
b->vfunc1(); // "ClassB::vfunc1()" ClassB把ClassA::vfunc1()覆盖了
b->vfunc2(); // "ClassC::vfunc2()" ClassC把ClassA::vfunc2()覆盖了
参考链接:
多态使用案例
先定义一个抽象基类,该基类中有些操作并未实现。然后定义非抽象的派生类,实现抽象基类中定义的操作。例如,虚函数就属此类情况。这时,派生类是抽象的基类的实现,即可看成是基类定义的延续。这也是派生类的一种常用方法。
//普通实现
class Calculator {
public:
int getResult(string oper)
{
if (oper == "+") {
return m_Num1 + m_Num2;
}
else if (oper == "-") {
return m_Num1 - m_Num2;
}
else if (oper == "*") {
return m_Num1 * m_Num2;
}
//如果要提供新的运算,需要修改源码
}
public:
int m_Num1;
int m_Num2;
};
void test01()
{
//普通实现测试
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
}
//多态实现
//抽象计算器类
//多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护
class AbstractCalculator
{
public :
virtual int getResult()
{
return 0;
}
int m_Num1;
int m_Num2;
};
//加法计算器
class AddCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 + m_Num2;
}
};
//减法计算器
class SubCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 - m_Num2;
}
};
//乘法计算器
class MulCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 * m_Num2;
}
};
void test02()
{
//创建加法计算器
AbstractCalculator *abc = new AddCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc; //用完了记得销毁
//创建减法计算器
abc = new SubCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
//创建乘法计算器
abc = new MulCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
虚析构与纯虚析构
非常好的一个参考链接:
含有纯虚析构的类属于抽象类,无法实例化对象,但可以指向子类对象。
抽象类无法实例化对象 Base b,new Base() 这两种方式都不行, 但是可以定义指针: Base * b。