网站建设费税收分类,怎么申请网站空间,百度seo推广,wordpress生产海报图不加virtual#xff0c;只能访问成员变量#xff0c;不能访问成员函数 文章目录 前言1 虚函数与多态不用虚函数的多态使用虚函数的多态 2 C虚函数注意事项 构成多态的条件C虚函数注意事项构成多态的条件什么时候声明虚函数 3 C虚析构函数的必要性总结 前言
本节阐述C中的多态… 不加virtual只能访问成员变量不能访问成员函数 文章目录 前言1 虚函数与多态不用虚函数的多态使用虚函数的多态 2 C虚函数注意事项 构成多态的条件C虚函数注意事项构成多态的条件什么时候声明虚函数 3 C虚析构函数的必要性总结 前言
本节阐述C中的多态polymorphism和虚函数virtual function首先来阐明一下什么是多态。
C中的多态指通过一个基类的指针或引用来调用其派生类对象的函数从而实现对不同子类对象的不同处理。基类指针指指向最原始那个类对象的指针。虚函数是C中一种函数的声明方式虚函数就是为了解决当一个基类指针调用派生子类对象函数的时候执行的是派生子类对象的函数和参数而不是基类的。 1 虚函数与多态
不用虚函数的多态
#include iostream
using namespace std;// 基类A
class A{
protected:int a_A;
public:// A(int a):a_A(a){}void display();
};
void A::display(){cout this is class A\nand value \n a_Aendl;
}
// 派生类 B
class B : public A{
public:B(int a): A(a){}void display();
};
void B::display()
{cout this is class B\nand value \n a_Aendl;
}int main()
{A *p new A(1);p-display();p new B(2);p-display();return 0;
}运行结果
this is class A
and value 1
this is class A
and value 2我们常态思维如果指针指向了派生类对象就应该使用派生类的成员变量和成员函数。但是该实例告诉我们当基类指针p指向派生类B的对象的时候虽然使用了派生类B的成员变量但是却没有使用它的成员函数导致输出结果啥也不是。
也就是说通过基类指针只能访问派生类的成员变量但是不能访问派生类的成员函数。
为了消除上述尴尬让基类指针能够访问派生类的成员函数C增加了虚函数Virtual Function。使用虚函数非常简单只需要在函数声明前面增加virtual关键字。
使用虚函数的多态
#include iostreamusing namespace std;class A{
protected:int a_A;
public:A(int a):a_A(a){}virtual void display();
};void A::display(){cout this is class A\nand value a_Aendl;
}class B : public A{public:B(int a): A(a){}void display();
};void B::display()
{cout this is class B\nand value a_Aendl;
}int main()
{A *p new A(1);p-display();p new B(2);p-display();return 0;
}运行结果
this is class A
and value 1
this is class B
and value 2只需要在函数申明的前面加上virtual便可以解决上述所说的尴尬。更全面地实现了多态。 C提供多态的目的是可以通过基类指针对所有派生类包括直接派生和间接派生的成员变量和成员函数进行“全方位”的访问尤其是成员函数。如果没有多态我们只能访问成员变量。
2 C虚函数注意事项 构成多态的条件
C虚函数注意事项
只需要在虚函数的声明处加上virtual关键字函数定义处可以加也可以不加只需要将基类中的函数声明为虚函数这样所有派生类中具有遮蔽关系的同名函数都将自动成为虚函数。当在基类中定义了虚函数但是在派生类没有定义新的函数来遮蔽次函数那么将使用基类的虚函数。只有派生类的虚函数覆盖基类的虚函数函数原型相同才能构成多态通过基类指针访问派生类函数。构造函数不能是虚函数。对于基类的构造函数他仅仅是在派生类构造函数中被调用这种机制不同于继承。也就是说派生类不继承基类的构造函数将构造函数声明为虚函数没有任何意义另外还有一种原因C中的构造函数用于创建对象时进行初始化工作在执行构造函数之前对象尚未创建完成虚函数表尚不存在也没有指向虚函数表的指针所以此时无法查询虚函数表也就不知道要调用哪个构造函数。析构函数可以声明为虚函数而且有时候必须要声明为虚函数
构成多态的条件
必须存在继承的关系继承关系中必须有同名的虚函数并且他们是覆盖关系函数原型相同存在基类的指针通过该指针调用虚函数通过基类指针只能访问从基类继承过去的成员不能访问派生类新增加的成员。
什么时候声明虚函数
首先看成员函数所在的类是否会作为基类。然后看成员函数在类的继承后有无可能被更改功能。
3 C虚析构函数的必要性
析构函数用于在销毁对象时进行清理工作可以声明为虚函数且有时候必要声明为虚函数。
#include iostreamusing namespace std;
// 基类 A
class A{
protected:char* str;public:A(){str new char[100];coutA contructorendl;}~A(){delete str;coutA destructorendl;}
};
// 派生类 B
class B: public A{
private:char* name;
public:B(){name new char[100];coutB contructorendl;}~B(){delete name;coutB destructorendl;}
};int main()
{A *pa new B();delete pa;coutendl;B *pb new B();delete pb;return 0;
}在上述代码中首先定义了基类A定义了A的成员变量protectedstr在构造函数中为str申请一块内存在析构函数中销毁str的这块内存定义了A的派生类BB中定义成员变量name在B的构造函数中为name申请一块内存并在B的析构函数中销毁这块内存。 在main函数中首先定义一个基类A的指针指向派生类B的对象然后释放这个指针。又定义一个指向派生类的指针指向派生类的对象再释放该指针。
运行结果
A contructor
B contructor
A destructorA contructor
B contructor
B destructor
A destructor很显然出现了问题内存泄露基类指针指向派生类对象在释放基类指针的时候并没有执行派生类的析构函数没有释放掉派生类中为成员变量name申请的内存空间只是执行基类的析构函数。
为什么delete pa; 不会调用派生类的析构函数 这里的析构函数不是虚函数通过指针访问非虚函数的时候编译器会根据指针的类型来确定要调用的函数也就是说指针指向哪个类就调用哪个类的函数。因此pa是基类的指针不管它指向基类对象还是派生类的对象始终都只调用基类的析构函数。
为什么delete pb; 会同时调用派生类和基类的析构函数 pb是派生类的指针编译器会根据它的类型匹配到派生类的析构函数在执行派生类的析构函数的过程中又会调用基类的析构函数。派生类析构函数始终会调用基类的析构函数这个过程是隐式完成的。
解决方法将基类的析构函数声明为虚函数。
class A{
public:A();virtual ~A();
protected:char* str;
};运行结果
A contructor
B contructor
B destructor
A destructorA contructor
B contructor
B destructor基类虚析构函数 联动当基类的析构函数声明为虚函数之后派生类的析构函数也会自动称为虚函数。此时编译器会忽略指针的类型而根据指针的指向来选择函数也就是说指针指向哪个类就调用哪个类的函数。pa和pb都指向派生类的对象所以会调用派生类的析构函数继而再调用基类的析构函数。因此也就解决了内存泄漏问题。
因此多半情况下基类的析构函数是虚函数 总结
使用了虚函数之后我们就可以通过定义一个基类指针遍历所有的派生类成员变量和成员函数很大程度简化了代码的复杂度。