虚析构
虚析构函数意义:通过父类指针释放子类对象。
虚析构函数用于指引 delete 运算符正确析构动态对象
eg:
#include <iostream>
using namespace std;
//基类
class A
{
public:
A()
{
cout << "A 的构造函数" << endl;
}
virtual ~A()//没有虚析构只会析构A
{
cout << "A 的析构函数" << endl;
}
};
//构造的顺序:沿着类的继承路径,往上找最顶层的父类,从最顶层的父类依次往下开始构造
//析构的顺序:从当前类开始沿着类的继承路径往上查找父类,依次析构,知道最顶层父类
class B:public A
{
public:
B()
{
cout << "B 的构造函数" << endl;
}
~B()
{
cout << "B 的析构函数"<< endl;
}
};
//通过基类指针释放派生类对象
void func(A* pa)
{
//pa 是A 的指针,delete释放的时候调用 A 的析构函数
//如果将 基类的 析构函数设置为虚函数, 则在调用delete释放的时候,
//调用的是传过来对象的析构函数
delete pa;
}
int main()
{
B *pb = new B;
func(pb);
return 0;
}
构造函数中是无法实现多态的
原因:虚函数指针在初始化的时候,执行到哪一个类的构造函数,该指针就指向当前类的虚函数表。
事例:
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "动物的构造函数"<< endl;
eat();
cout << "-------------" << endl;
}
virtual void eat()
{
cout << "动物吃饭" << endl;
}
void sleep()
{
cout << "动物睡觉 "<< endl;
}
private:
int a;
};
class Cat:public Animal
{
public:
Cat()
{
cout << "猫的构造函数" << endl;
eat();
cout << "-----------------" << endl;
}
void eat()
{
cout << "猫吃鱼" << endl;
}
void sleep()
{
cout << "猫睡觉" << endl;
}
private:
int b;
};
//构造函数和中是无法实现多态的,
//原因:
/*
虚函数指针在初始化的时候,是分布进行初始化的,执行到哪一个类的构造函数,
该指针就指向这个类的虚函数表
对于Cat
1、先调用Animal的构造函数,则vfptr 指向Animal的虚函数表
2、调用Cat 的构造函数,则vfptr指向Cat的虚函数表
*/
int main()
{
Cat c;
return 0;
}
用基类指针操作派生类数组
在派生类对象数组中,数组单个元素开头都以 vfptr (虚函数指针)开始后面才接着继承的成员和自身的成员,用派生类指针对派生类数组进行指针加减操作时,指针每 + 或 - 的时候,指针移动的距离都是派生类自身的长度,所以可以正常使用;
然而在用基类指针对派生类数组进行加减操作时,指针每 + 或 - 的时候,指针移动的距离都是基类的长度,而派生类一般会比基类多出自身成员的长度(派生类比基类内存占的多,地址跨越要长),在实现多态时,不能准确的找到当前对象的vfptr指针,多态的实现也就不可能了。
#include <iostream>
using namespace std;
class Parent
{
public:
Parent(int a)
{
this->a = a;
}
virtual void show()
{
cout << "a = " << a << endl;
}
protected:
int a;
};
class Child:public Parent
{
public:
Child(int a, int b):Parent(a)
{
this->b = b;
}
virtual void show()
{
cout << "a = " << a << ", b = " << b << endl;
}
private:
int b;
};
int main()
{
Child c[5] = {Child(1,2), Child(3,4), Child(5,6), Child(7,8), Child(9,10)};
/*正常显示
Child *pc = c;
for(int i = 0; i < 5; i++)
pc[i].show();
*/
//基类指针指向派生类数组
Parent *p = c;
//基类指针和派生类指针步长不一样,用基类指针操作派生类数组的时候会产生问题
for (int i = 0; i < 5; i++)
p[i].show();//只显示第一个对象的show,后面就出错
return 0;
}
指针加减的操作知识
int main()
{
double *pd = (double*)1000;//pd指向地址为1000 的内存空间
double *p1 = pd + 1;//1000+8=1008, pd为double类型的指针,double类型占8字节的空间,指针+1:指针会跨越一个自身数据类型的长度
int* p2 = (int*)pd + 1;//1004,p2 是将pd强制转化成int型的指针 然后+1 取值,int占4字节空间,所以地址跨越长度为4
int p3 = (int)pd + 1;//1001,将pd强制转化成int型数据(数据类型改变)赋值给p3,+1则变成正常int型数据的+1即 1000+1=1001
printf("p1 = %d, p2 = %d, p3\n", p1, p2, p3);
return 0;
}