C++入门学习:虚析构、构造中不能实现多态、基类指针指向派生类数组的弊端

虚析构

虚析构函数意义:通过父类指针释放子类对象。

虚析构函数用于指引 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值