C++类开始的重新学习记录3
第三部分——多态
1、分类(C++中的多态多指动态)
1、静态多态
-函数重载、运算符重载,发生在编译阶段的多态。
2、动态多态
-派生类和虚函数实现的,发生在运行阶段的多态。
2、特点
代码组织清晰
可读性强
利于前期和后期维护
3、多态使用
1、两种多态实现方式
父类引用,执行子类对象
class Base
{
public:
virtual void func()
{
cout << "Base func()" << endl;
}
};
class Son :public Base
{
public:
void func()
{
cout << "Son func()" << endl;
}
};
void doFunc(Base &b)
{
b.func();
}
void test()
{
Son s;
//父类引用,执行子类对象
doFunc(s);
}
父类指针,执行子类对象
void test()
{
//父类指针,执行子类对象
Base *b = new Son;
b->func();
}
2、对比普通实现和多态实现(使用具体例程)
普通:
class Calculator
{
public:
Calculator()
{
m_Num1 = 0;
m_Num2 = 0;
}
Calculator(int num1, int num2)
{
m_Num1 = num1;
m_Num2 = num2;
}
int GetNum1()
{
return m_Num1;
}
int GetNum2()
{
return m_Num2;
}
//重点函数
int Work(char op)
{
if (op == '+')
{
return m_Num1 + m_Num2;
}
else if (op == '-')
{
return m_Num1 - m_Num2;
}
else if (op == '*')
{
return m_Num1 * m_Num2;
}
return 0;
}
private:
int m_Num1;
int m_Num2;
};
void test()
{
Calculator c(10, 10);
cout << c.GetNum1() << "+" << c.GetNum2() << "=" << c.Work('+') << endl;
cout << c.GetNum1() << "-" << c.GetNum2() << "=" << c.Work('-') << endl;
cout << c.GetNum1() << "*" << c.GetNum2() << "=" << c.Work('*') << endl;
}
多态:
class BaseCalculator
{
public:
BaseCalculator()
{
m_Num1 = 10;
m_Num2 = 10;
}
BaseCalculator(int num1, int num2)
{
m_Num1 = num1;
m_Num2 = num2;
}
int GetNum1()
{
return m_Num1;
}
int GetNum2()
{
return m_Num2;
}
//重点函数,将其写成虚函数
virtual int Work()
{
return 0;
}
private:
int m_Num1;
int m_Num2;
};
class AddCalculator:public BaseCalculator
{
int Work()
{
return GetNum1() + GetNum2();
}
};
class SubCalculator :public BaseCalculator
{
int Work()
{
return GetNum1() - GetNum2();
}
};
class MulCalculator :public BaseCalculator
{
int Work()
{
return GetNum1() * GetNum2();
}
};
void test()
{
//注意释放堆区数据
//此处为父类指针执行子类对象,调用子类成员函数
BaseCalculator *bc = new AddCalculator;
cout << bc->GetNum1() << "+" << bc->GetNum2() << "=" << bc->Work() << endl;
delete bc;
bc = new SubCalculator;
cout << bc->GetNum1() << "-" << bc->GetNum2() << "=" << bc->Work() << endl;
delete bc;
bc = new MulCalculator;
cout << bc->GetNum1() << "*" << bc->GetNum2() << "=" << bc->Work() << endl;
delete bc;
bc = NULL;
}
4、多态条件
1、有继承
2、子类重写父类中的虚函数(返回值,名称,参数列表完全相同)
3、父类指针或引用执行对象
5、多态底层实现
1、vfptr——虚函数指针
2、vftable——虚函数表
3、子类重写虚函数时,子类中vftable内部会被全部替换成子类的虚函数地址
4、调用虚函数时走的虚函数表,从谁调用就会调用谁
6、纯虚函数
1、拥有纯虚函数的类成为抽象类
2、抽象类不能实例化
3、子类必须重写纯虚函数,否则也是抽象类,不能实例化
4、形式
virtual void func() = 0;
7、虚析构、纯虚析构
1、先看一个有问题的代码
class Base
{
public:
Base()
{
cout << "Base构造" << endl;
}
~Base()
{
cout << "Base析构" << endl;
}
};
class Son :public Base
{
public:
Son()
{
name = new string("Tom");
cout << "Son构造" << endl;
}
~Son()
{
if (name != NULL)
{
cout << "Son析构" << endl;
delete name;
name = NULL;
}
}
string *name;
};
void test()
{
Base *b = new Son;
//父类指针析构,不会调用子类析构,导致内存没有释放
delete b;
}
看结果,缺少Son的析构:
2、Son没有析构,原因就是因为父类指针析构,子类没有调用。
解决方法,将父类析构写为虚函数
virtual ~Base()
{
cout << "Base析构" << endl;
}
3、纯虚析构
virtual ~Base() = 0;
纯虚析构声明也要实现(类外实现)
Base::~Base()
{
cout << "Base纯虚析构" << endl;
}