文章目录
虚函数
意义: 通过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个字节
内存之间也有差别。