c++中的虚函数与纯虚函数

虚函数是C++很重要的一部分,C++的多态就是靠它完成的


首先怎么让一个函数成为虚函数

很简单,在函数声明前面加上 virtual 就可以了。如:

class BaseClass
{
public:
	virtual void Func()//虚函数
	{
		printf("Func In BaseClass\n");
	}
};


然后我们看看虚函数究竟有什么特别之处

假设我们通过继承得到一个子类:

class ChildClass1 : public BaseClass
{
public:
	void Func()
	{
		printf("Func In ChildClass\n");
	}
};

然后再在main函数输入测试代码:

int main()
{
	ChildClass1 child;
	BaseClass*  pClass = &child;
	pClass->Func();

        return 0;
}

看看结果:

明明是BaseClass* 类型的 pClass调用的居然是ChildClass1类的方法


当然你可能认为正很正常,毕竟你是把 ChildClass1 类型的child 的地址复制给了pClass

但如果Func不是虚函数呢?

#include <stdio.h>
class BaseClass
{
public:
	void Func() //注意:现在Func不是虚函数
	{
		printf("Func In BaseClass\n");
	}
};

class ChildClass1 : public BaseClass
{
public:
	void Func()
	{
		printf("Func In ChildClass\n");
	}
};

int main()
{
	ChildClass1 child;
	BaseClass*  pClass = &child;
	pClass->Func();

        return 0;
}

结果却是

之后我们来看看虚函数的用处

1.将析构函数声明为虚函数

#include <stdio.h>
class BaseClass
{
public:
	virtual ~BaseClass()//析构函数是虚函数
	{
		printf("Func In BaseClass\n");
	}
};

class ChildClass1 : public BaseClass
{
public:
	~ChildClass1()
	{
		printf("Func In ChildClass\n");
	}
};

int main()
{
	ChildClass1* child = new ChildClass1();
	BaseClass*  pClass = child;
	delete pClass;

        return 0;
}

我们会的到结果:

看我们 delete 掉 BaseClass* 类型的 pClass ,因为 pClass  的值是由 ChildClass1* 类型的 child 复制得到的,所以会调用ChildClass1的析构函数(就是说他不只调用了父类的析构函数,也会调用子类的析构函数)


如果不是虚函数呢?

#include <stdio.h>
class BaseClass
{
public:
	~BaseClass() //注意:现在不是虚函数
	{
		printf("Func In BaseClass\n");
	}
};

class ChildClass1 : public BaseClass
{
public:
	~ChildClass1()
	{
		printf("Func In ChildClass\n");
	}
};

int main()
{
	ChildClass1* child = new ChildClass1();
	BaseClass*  pClass = child;
	delete pClass;

        return 0;
}


结果只调用了父类的析构函数


有时候我们会在子类里new一些变量出来,很多时候都是在子类的析构函数里才把他们delete掉

而如果你new一个这样的子类出来,然后将它转型为父类指针delete掉,就需要将基类的析构函数声明为虚函数。

(就像我等下说的学校职工管理系统的例子,就需要用delete基类指针的方式释放new出来的子类)


2.我们可以用一个父类指针数组管理多个不同的子类(当然,实际编程中多数用vector容器)

#include <stdio.h>
class BaseClass
{
public:
	virtual ~BaseClass() 
	{
		printf("~BaseClass\n");
	}

	virtual void Func()
	{
		printf("Func In BaseClass\n");
	}
};

class ChildClass1 : public BaseClass
{
public:
	~ChildClass1()
	{
		printf("~ChildClass1\n");
	}
	void Func()
	{
		printf("Func In ChildClass1\n");
	}
};

class ChildClass2 : public BaseClass
{
public:
	~ChildClass2()
	{
		printf("~ChildClass2\n");
	}
	void Func()
	{
		printf("Func In ChildClass2\n");
	}
};

int main()
{
	BaseClass* classGroup[2];

	classGroup[0] = new ChildClass1();
	classGroup[1] = new ChildClass2();

	for(int i = 0; i<2 ; i++)
		classGroup[i]->Func();

	for(int j = 0; j<2 ; j++)
		delete classGroup[j];

	return 0;
}

结果得到:

也就是说,当我们有很多个不同的子类需要统一管理的时候,虚函数就有了大用处。


具体的例子就是你为学校写了个职工管理系统,尽管学校里面的职工有很多种(老师、保安、清洁员、饭堂阿姨......)你每一种写了一个类,但都继承于Employee这个类

class Employee  
{  
public:  
  
    //干活, “=0“ 是纯虚函数的意思,我等下讲  
    virtual Work() = 0;  
  
    //拿工资  
    virtual GetSalary() = 0;  
}; 

class 老师类 : public Employee
{
	...
};

class 保安类 : public Employee
{
	...
};

class 保洁员类 : public Employee
{
	...
};

于是你就只需要来一个 Employee数组 或者 Employee vector容器,每多一个职工就对应着new出一个具体的子类实例。将所有不同的子类实例都放进去,然后来一个循环让他们干活,再来一个循环然他们拿工资

你可以这样做:

std::vector<Employee*> group;

Employee* pNewEmployee;

//新来一个老师
pNewEmployee = new 老师类();
group.push_back(pNewEmployee);

//新来一个保安
pNewEmployee = new 保安类();
group.push_back(pNewEmployee);

//新来一个保洁员
pNewEmployee = new 保洁员类();
group.push_back(pNewEmployee);
至于循环那里我就不说了,自己可以想想

最后讲讲纯虚函数

纯虚函数就是一种特殊的虚函数,它拥有虚函数的所用功能(当然也有不同于虚函数的地方)

我们这样将一个函数声明为纯虚函数:

class AClass
{
public:
	virtual void Func() = 0;
};
(也就是虚函数后面多一个 ” = 0 “而已)

如果一个类中有纯虚函数,那么这个类就不能被实例化

也就是:

class AClass
{
public:
	virtual void Func() = 0;
};

int main()
{
	//会报错,编译不通过
	AClass a; 

	//也会报错,编译不通过
	AClass* b = new AClass(); 

	return 0;
}

必须有一个子类实现他的纯虚函数,才能被实例化

class AClass
{
public:
	virtual void Func() = 0;
};
class ChildClass :public AClass
{
public:
	void Func() {}
};

int main()
{
	//会报错,编译不通过
	//AClass a; 

	//也会报错,编译不通过
	//AClass* b = new AClass(); 

	//这样才能编译通过
	ChildClass a; 
	ChildClass* b = new ChildClass(); 

	return 0;
}

值得一提的是子类必须 实现父类的 所有纯虚函数才能被实例化

class AClass
{
public:
	virtual void Func() = 0;
};
class ChildClass :public AClass
{
public:
	void Func(); //如果只有声明而没有实现它
};

int main()
{
	//编译不通过
	ChildClass a; 
	ChildClass* b = new ChildClass(); 

	return 0;
}

class AClass
{
public:
	virtual void Func() = 0;
};
class ChildClass :public AClass
{
public:
	//或者你连声明都没有。。。
};

int main()
{
	//编译不通过
	ChildClass a; 
	ChildClass* b = new ChildClass(); 

	return 0;
}

PS:本人喜欢用 printf 多于 cout(因为现在学c++都不怎么打控制台程序了,C语言时打习惯了printf......)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值