虚函数和多态

一、虚函数和虚函数表

用virtual修饰的函数叫虚函数
虚函数表:函数指针,存储所有虚函数首地址


普通函数对类所占内存无影响,虚函数会影响,但无论有多少虚函数,只增加一个指针大小的字节(即四个字节),空的类或者结构体 占用一个字节作为标识 一旦有数据就不需要这一个字节
对于虚函数在类外实现就不需要virtual只需要类名限定即可

多态:同一个调用的不同结果,注意弄清什么时候调用哪个函数
多态的必要条件 1父类必须要有虚函数 2子类必须采用公有继承 3必须要有指针的使用

class Man
{
public:
	void WC1()
	{
		cout << "manWC1" << endl;
	}
	virtual void WC2()//父类必须要有virtual
	{
		cout << "manWC2" << endl;
	}
protected:
};
class Woman:public Man
{
public:
	//要求子类也有相同函数,但函数可以不带virtual
	void WC1()
	{
		cout << "woWC1" << endl;
	}
	void WC2()
	{
		cout << "woWC2" << endl;
	}
protected:
};
void testVirtual()
{
	//正常访问不存在多态 就近原则
	//非正常赋值 子类对象初始化父类指针
	Man* pt = new Woman;
	//有virtual看对象类型 没有virtual看指针类型
	pt->WC1();//父类
	pt->WC2();//子类
	pt = new Man;
	pt->WC2();//父类
}

二、虚析构函数

用子类对象初始化父类指针,需要使用虚析构函数进行内存释放

class parent
{
public:
	virtual ~parent()
	{
		cout << "父类析构函数" << endl;
	}
	void print() {}
protected:

};
class son :public parent
{
public:
	
	~son()
	{
		cout << "子类析构函数" << endl;
	}
};

final与override

class A
{
public:
	//final禁止重写
	//final子类不能子类不能存在同名函数
	virtual void print() final
	{
		cout << "重写函数虚函数才有" << endl;
	}
	virtual void printData()
	{}
};
class B :public A
{
	void printData() override//强制重写 标识作用 检查父类中是否存在当前虚函数
	{
		cout << "当前函数是重写函数" << endl;
	}
};

三、纯虚函数和ADT

纯虚函数:也是虚函数 只是没有函数体 virtual print()=0 要在类中这样写
抽象类:拥有至少一个出虚函数的类 不能构建对象 但可以构建对象指针
纯虚函数就是做ADT的过程(abstract data typpe 抽象数据类型)即搭建框架 
纯虚函数没有被重写 无论被继承多少次都是纯虚函数

以栈为例

class stack
{
public:
	//描述好父类中所有的属性
	virtual void push(int data) = 0;
	virtual void pop() = 0;
	virtual int top() const = 0;
	virtual bool empty() const = 0;
	virtual int size() = 0;
};
//子类想创建对象,必须重写父类的纯虚函数
//ADT具有强迫性,所有子类重写函数必须和父类一样
class arrayStack :public stack
{
public:
	void push(int data) {}
	void pop() {}
	int top() const {
		return 1;
	}
	bool empty() {
		return false;
	}
	int size() {
		return 1;
	}
	//可以增加别的函数和成员
};
void testVirtual(stack* pStack)
{
	pStack->push(1);
	while (!pStack->empty())
	{
		cout << pStack->top();
		pStack->pop();
	}
}

四、类型转换

所谓的更为安全的转换方法

(1)const_cast

/*const_cast<要转换的类型>(要转换的目标)
1去掉const属性(提供一个可以修改的接口可以去操作const属性的变量
2加上const属性(用的比较少)
*/
class A
{
public://处理字符串常属性的问题 可以传变量或者常量
	A(const char* str) :str(const_cast<char*>(str)) {}
protected:
	char* str;
};
class B
{
public://处理字符串常属性的问题 可以传变量或者常量
	B(int num) :num(num) {}
	void print() {}
protected:
	int num;
};
/*
	const int num = 1;
	//const int* pNum = &num;
	int* pNum = const_cast<int*>(&num);
	*pNum = 33;//num实际没变 
	cout << num;
	//操作类指针
	const B* pB = new B(0);
	B* ppB = const_cast<B*>(pB);
	ppB->print();
	//操作常量引用
	int num1;
	const int& pNum1 = num1;
	int& ppNum1 = const_cast<int&>(pNum1);
*/

(2)static_cast

//static_cast<要转换的类型>(要转换的目标)
// 1基本数据类型转换
// 2把空指针转换为目标类型
// 3把任何类型变量转换为void类型
// 4用在类上面的转换(基类和派生类的转换)
//  4,1上行转换(从子到父 指针或引用转换)安全
//  4.2上行转换(从父到子 指针或引用转换)
// 注意:不能转换const
/*
	//1基本数据类型
	char cNum = 'S';
	int iNum = static_cast<int>(iNum);
	//2空类型指针
	double* p = new double;
	void* pVoid = static_cast<void*>(p);
	//3const类型转换
	int x = 0;
	const int constNum = static_cast<const int>(x);
	//4错误用法
	const int xx = 0;
	//int* p = static_cast<int*>(&xx); 去掉const必须用const_cast
*/

(3)dynamic_cast

//dynamic_cast
// 1上行转换 和static_cast一样
// 2下行转换 dynamic_cast更安全
// 3交叉转换 多继承
class MM
{
public:
	MM(string mmName = "父类") :mmName(mmName) {}
	virtual void print() { cout << mmName; }
protected:
	string mmName;
};
class Girl:public MM
{
public:
	Girl(string gName="子类") :gName(gName) {}
	void print() { cout << gName; }
	void printData() {}
	string gName;
protected:
};
/*  
    MM* pM = new MM;
	Girl* pG = new Girl;
	//1上行
	MM* pSM = static_cast<MM*>(pG);
	MM* pDM = dynamic_cast<MM*>(pG);
	//2下行
	Girl* pSG = static_cast<Girl*>(pM);//不存在virtual不会报错
	Girl* pDG = dynamic_cast<Girl*>(pM);//检查父类中是否有virtual
	//不存在 类型转换直接错误
	//pSG->printData() 下行转换 调用子类中父类没有的函数报错
*/
//交叉转换 实际就是C继承了A B 然后A B间互相赋值
class A
{
public:
	virtual void print()
	{
		cout << "A" << endl;
	}
};
class B
{
public:
	virtual void print()
	{
		cout << "B" << endl;
	}
};
class C :public A,public B
{
public:
	void print()
	{
		cout << "C" << endl;
	}
};
//3交叉转换
	//A* a = new C;
  //B* b = dynamic_cast<B*>(a);

(4)reinterpret_cast

在哈希表会用,其余用的很少 ,用来转换函数指针和数字,

如int print(){}

int num=reinterpret_cast<int>(print);//num这个数字储存了函数指针print的地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值