c++ (虚函数和纯虚函数)


虚函数

意义: 通过virtual修饰的函数,函数在声明时需要进行定义,在子类可以覆盖,或者不进行重定义继续使用继承来的虚函数.通过virtual标识的函数可以实现多态,即通过(引用&,和new)的对象可以实现动态调用自己被引用(&)的函数或被(new)创建的类型 内函数的调用(动态联编);

1.不实现子类

子类继承父类的定义
代码如下(示例):

#include <iostream>
using namespace std;
class A{
	public:
		virtual void show(){
			cout<<"A::show\n";
		}
};
class B:public A{
	public:
		//void show();
};
int main()
{
	B b;
	b.show();
	return 0;
}

运行结果为:
A::show

2.重写父类

代码如下(示例):

#include <iostream>
using namespace std;
class A{
	public:
		virtual void show(){
			cout<<"A::show\n";
		}
};
class B:public A{
	public:
		void show(){
			cout<<"B::show\n";
		}
};
int main()
{
	B b;
	b.show();
	return 0;
}

运行结果为:
B::show

子类可以在函数前不写virtual修饰,但一般写上vitual,因为可以让别人或自己可以知道这个是虚函数.

3.和普通函数的区别

1.普通函数
	#include <iostream>
using namespace std;
class A{
	public:
		void show(){
			cout<<"A::show\n";
		}
};
class B:public A{
	public:
		void show(){
			cout<<"B::show\n";
		}
};
int main()
{
	A *a=new B();
	a->show();
	B b_;
	A &a_=b_;
	a_.show();
	delete a;
	return 0;
}

运行结果为:

A::show
A::show

2.虚函数的类
#include <iostream>
using namespace std;
class A{
	public:
	virtual	void show(){
			cout<<"A::show\n";
		}
};
class B:public A{
	public:
		void show(){
			cout<<"B::show\n";
		}
};
int main()
{
	A *a=new B();
	a->show();
	B b_;
	A &a_=b_;
	a_.show();
	delete a;
	return 0;
}

运行结果为:

B::show
B::show


对比可发现经过virtual修饰的函数和普通函数,在使用&和*的时候有这不一样的效果,虚函数可以让 A *a=new B();的a->show();调用B的show,可以让A &a_=b_;的a_.show()调用B的show().指向准确的对象.

纯虚函数

意义:对声明的函数进行virtual修饰并且不定义在函数()后面添加=0,他会让类变成抽象类(不可被实例化的类,不可创建对象),与虚函数不同的是,它在声明的时候不需要定义.在它的子类可以不定义(如果在子类不定义的话,子类也变成抽象类了,不可创建对象),如果子类定义了纯虚函数,那么这个类就可以被创建.

如果子类(B)又有子类(C) 我把基类比作class A,把子类比作class B,把子类的子类比作class C.

class A{
	public:
	virtual void show()=0;
};
class B:public A{
};
class C:public B{
};

如果B实现了show(),那么就可以把B里面的show()理解为虚函数,C可以不重写show()也不会是抽象类.如果B没有实现show(),那么B就是抽象类了,C类如果想要被创建就需要把show()函数给实现了(定义了).

1.class B实现纯虚函数show()

#include <iostream>
using namespace std;
class A{
	public:
	virtual	void show()=0;
};
class B:public A{
	public:
		void show(){
			cout<<"B::show\n";
		}
};
int main()
{
	A *a=new B();
	a->show();
	delete a;
	return 0;
}

运行结果为:
B::show

2.class B不实现纯虚函数show()

#include <iostream>
using namespace std;
class A{
	public:
	virtual	void show()=0;
};
class B:public A{
	public:
		//void show();
};
int main()
{
	A *a=new B();
	a->show();
	delete a;
	return 0;
}

运行结果为:
编译错误,抽象类不可以被实例化

3.class B实现纯虚函数show(),class C不实现纯虚函数show()

#include <iostream>
using namespace std;
class A{
	public:
	virtual	void show()=0;
};
class B:public A{
	public:
		void show(){
			cout<<"B::show\n";
		}
};
class C:public B{
	
};
int main()
{
	A *a=new C();
	a->show();
	delete a;
	return 0;
}

运行结果为:
B::show
这里可以理解为实现了纯虚函数,就会把纯虚函数降为虚函数.这样后面继承的C就可以不重写show也可以创建对象

4.class B实现纯虚函数show(),class C实现纯虚函数show()

#include <iostream>
using namespace std;
class A{
	public:
	virtual	void show()=0;
};
class B:public A{
	public:
		void show(){
			cout<<"B::show\n";
		}
};
class C:public B{
	public:
		void show(){
			cout<<"C::show\n";
		}
};
int main()
{
	A *a=new C();
	a->show();
	delete a;
	return 0;
}

运行结果为:
C::show

总结与提醒

1.如果把析构函数设置为纯虚函数这里有所不同
需要实现(定义)析构函数

#include <iostream>
using namespace std;
class A{
	public:
	virtual void show()=0; 
	virtual ~A()=0;
};
A::~A(){} //在外部定义
class B:public A{
	public:
		void show(){
			cout<<"B::show\n";
		}
};
int main()
{
	A *a=new B;
	a->show();
	delete a;
	return 0;
}

有人可能会问我为什么要在析构函数上面声明纯虚函数?
再给你看一个例子

#include <iostream>
using namespace std;
class A{
	public:
	virtual void show()=0;
	~A(){
		cout<<"~A\n";
	}
};

class B:public A{
	public:
	void show(){
		cout<<"B::show\n";
	}
	~B(){
		cout<<"~B\n";
	}
};
int main()
{
	A *a=new B;
	a->show();
	delete a;
	return 0;
}

运行结果为:

B::show
~A

啊?我的~B怎么没有打印!!!

这个是个很危险的隐藏问题,这个可能会导致内存泄漏

#include <iostream>
using namespace std;
class T{
	private:
		int *a;
	public:
		T(){
			a=new int(0);
		}
		~T(){
			delete a;
			cout<<"~T\n";
		}
};
class A{
	private:
		T t;
	public:
	~A(){
		cout<<"~A\n";
	}
};
class B:public A{
	private:
		T t;
	public:
	~B(){
		cout<<"~B\n";
	}
};
int main()
{
	A *a=new B;
	delete a;
	return 0;
}

运行结果为:

~A
~T

这个应该有两次~T呀?一次为B的~T一次为A的~T.但是它只析构了A的T

发生了内存泄漏!!
改进方法为把A基类的析构函数变成纯虚函数,*a被delete的时候才会按照从子类到基类的顺序进行全部析构.

#include <iostream>
using namespace std;
class T{
	private:
		int *a;
	public:
		T(){
			a=new int(0);
		}
		~T(){
			delete a;
			cout<<"~T\n";
		}
};
class A{
	private:
		T t;
	public:
	virtual ~A()=0;
};
A::~A(){
	cout<<"~A\n";
}
class B:public A{
	private:
		T t;
	public:
	~B(){
		cout<<"~B\n";
	}
};
int main()
{
	A *a=new B;
	delete a;
	return 0;
}

虚函数也可以 :(将A的~A修饰为virtaul)

#include <iostream>
using namespace std;
class T{
	private:
		int *a;
	public:
		T(){
			a=new int(0);
		}
		~T(){
			delete a;
			cout<<"~T\n";
		}
};
class A{
	private:
		T t;
	public:
	virtual ~A(){
		cout<<"~A\n";
	};
};
class B:public A{
	private:
		T t;
	public:
	~B(){
		cout<<"~B\n";
	}
};
int main()
{
	A *a=new B;
	delete a;
	return 0;
}

运行结果为:

~B
~T
~A
~T

隐藏小关卡

#include <iostream>
using namespace std;
class A{
	public:
	~A(){
		cout<<"~A\n";
	};
};
int main()
{
	cout<<sizeof(A);
	return 0;
}
#include <iostream>
using namespace std;
class A{
	public:
	virtual ~A(){
		cout<<"~A\n";
	};
};
int main()
{
	cout<<sizeof(A);
	return 0;
}

没有被virtaul修饰的~A的占用1个字节,但是被virtaul修饰的占用了8个字节

内存之间也有差别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值