当前位置:首页 » 编程语言 » c语言虚函数

c语言虚函数

发布时间: 2022-07-02 16:01:49

‘壹’ 为什么C++的构造函数不可以是虚函数,而析构

1、为什么构造函数不可以是虚函数
①从存储空间角度

虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。

②从使用角度

虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。

虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。

③构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它。但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。

④从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数

从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数

⑤当一个构造函数被调用时,它做的首要的事情之一是初始化它的V P T R。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码- -既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。

所以它使用的V P T R必须是对于这个类的V TA B L E。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内, V P T R将保持被初始化为指向这个V TA B L E, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置V P T R指向它的 V TA B L E,等.直到最后的构造函数结束。V P T R的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生 类顺序的另一个理由。

但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置V P T R指向它自己的 V TA B L E。如果函数调用使用虚机制,它将只产生通过它自己的V TA B L E的调用,而不是最后的V TA B L E(所有构造函数被调用后才会有最后的V TA B L E)。
2、为什么析构函数可以是虚函数
编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于派生类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。
所以建议的方式是将析构函数声明为虚函数。如果你使用MFC,并且以CObject或其派生类为基类,那么MFC已经为你做了这件事情;CObject的析构函数是虚函数。一个函数一旦声明为虚函数,那么不管你是否加上virtual 修饰符,它在所有派生类中都成为虚函数。但是由于理解明确起见,建议的方式还是加上virtual 修饰符。
C++不把虚析构函数直接作为默认值的原因是虚函数表的开销以及和c语言的类型的兼容性。有虚函数的对象总是在开始的位置包含一个隐含的虚函数表指针成员。如果是对于MFC类CPoint和CSize这样的小型类,增加一个指针就增加了很多内存占用,而且使得其内存表示和基类POINT和SIZE不一致。

‘贰’ 什么函数不能声明为虚函数

inline, static, constructor ,template 函数都不能 为虚函数,而析构函数可以。
为什么呢:
inline: 编译器替换; 而虚函数是为了解决运行期间绑定。
static:class 成员; 编译期间就给class了。
constructor: 构造函数表示要生成一个class的object;假设是virtual的,那就说不知道这个实例化derived还是based的class。但是类型实例化必须在编译期确定(否则编译器不知道到底是什么对象了)
template:模板实例是在 compile-time,virtual就意味着在run-time确定。这让编译器设计者为难了,这就是说虚函数表要指向各种版本的 template function 实例,代码设计者confuse,编译器设计者也觉得蛮烦。

‘叁’ C++,虚函数,

如果派生类定义了一个与基类同名,但具有不同参数的函数,这个函数究竟有没有被加入到派生类的虚函数表中?答案是yes。注意,不是虚函数就不来凑这个热闹了。

#include"pch.h"
#include<string>
#include<iostream>

usingstd::cout;
usingstd::endl;
classbrass
{
private:
inta;
public:
brass():a(5)
{
};
virtualvoidwith()
{
cout<<a<<endl;
}
};

classbrassplus:publicbrass
{
private:
intb;
public:
brassplus():b(6)
{
};
virtualvoidwith()
{
cout<<"1:"<<b<<endl;
}
virtualvoidwith(inti)
{
cout<<"2:"<<b<<endl;
}
};
intmain()
{
brass*a;
brassplusb;

a=(brass*)&b;
a->with();
b.with(1);
b.brass::with();
}

口说无凭,现在我们就上vs2017调试器,揭开虚函数表的面纱。首先用鼠标在编辑器左栏下断点,断点下在brassplus b;一行,然后点击“本地Windows调试器”进入调试模式。

‘肆’ c语言编程,通过虚函数求派生类对象——某个圆柱体的体积。求助,怎么编写啊

#include<iostream>
#define PI 3.14159

class Circle
{
public:
float radius;
public:
Circle(float r)
{
radius = r;
};
virtual float area()
{
return PI * radius * radius;
};
virtual float volume()
{
return 0.0;
};
};

class Sphere : public Circle
{
public:
Sphere(float r)
{
radius = r;
};
virtual float area()
{
return 4 * PI * radius * radius;
};
virtual float volume()
{
return 4 * PI * radius * radius * radius / 3.0;
};
};

class Column : public Circle
{
public:
float height;
public:
Column(float r,float h)
{
radius = r;
height = h;
};
virtual float area()
{
return 2 * PI * radius * ( radius + height );
};
virtual float volume()
{
return PI * radius * radius * height;
};
};

int main()
{
Sphere s(20.0);
Column c(10.0,30.0);
cout << "s.area = "<<s.area()<<endl;
cout << "s.volume= "<<s.volume()<<endl;
cout << "c.area = "<<c.area()<<endl;
cout << "c.volume= "<<c.volume()<<endl;

return 0;
}

‘伍’ C语言中void DigDisplay()是什么意思

您好,很高兴回答您的问题。
由于没有看到其他的内容所以只能从结构上来说明相关含义。
void digdisplay()表示的是这个函数的定义,其中void表示函数不带任何的返回值,digdisplay表示的是函数名,括号中表示函数的参数,这里是空的,所以表示不带任何的返回值。
以上就是我的回答,敬请指正。

‘陆’ C++里比C里多出了继承,多态,虚函数是啥回事求通俗解答…………谢谢!

C++是一门从C发展而来的编程语言,都是诞生于贝尔实验室,因此是一脉相承的关系。
既然是发展,那就要多了些东西以适应更加复杂的编程要求咯。

就好比你最早买的U盘是usb1.0的,后来有了usb2.0,现在是usb3.0,显然高版本的要比低版本的有更多的功能。同样的道理C++就比C语言多了一些功能(严格地说叫语言特性)。多出来的这部分内容有2大块,一个叫面向对象,也就是你说的封装、继承、多态;另外一部分叫泛型编程。

C++首先比C多出了类这个概念,你要先明白类是怎么回事。

接下来说继承,你老爸留下了一大笔遗产,你可以继承并发扬光大,取其精华,去其糟粕。
同理,别人写了一个C++的类A,你拿过来继承一下,得到属于你的类B,你的类B就有了别人的那部分功能,同时你再添的东西,于是类B把类A发扬光大了。
举个现实中的例子,比如我现在用的搜狗输入法具有换皮肤的功能,那么这个功能就是一个类A,过了一段时间,新来了一个哥们,他想开发一个定时自动换皮肤的功能,他不需要把搜狗输入法全部重新写一遍,他只要把原来的功能A继承过来,在里面添加一个自动换皮肤的函数,于是就得到了新的类B。现在好了,原本要重新写的程序,现在只要添点东西就OK,这不是节省了很多编程人员的精力了吗。所以说继承能够让你很快地用上别人的东西,同时添加自己的东西。

建议你看看 Head First 系列图书,里面有很多浅显易懂的例子,你就会对面向对象编程有很好的了解了。当然,这一系列的书都价格不菲。

Head First深入浅出设计模式
http://book.douban.com/subject/1488876/
Head First深入浅出面向对象分析与设计(中文版)
http://book.douban.com/subject/3530721/

‘柒’ C++中的virtual关键字是什么怎么用

virtual是定义C++中虚函数的关键字 。

1、virtual关键字的作用:

c++中的函数调用默认不适用动态绑定。要触发动态绑定,必须满足两个条件:第一,指定为虚函数;第二,通过基类类型的引用或指针调用。由此可见,virtual主要功能是实现动态绑定。

2、virtual关键字的使用情况:

virtual可用来定义类函数和应用到虚继承。

友元函数 构造函数 static静态函数 不能用virtual关键字修饰;

普通成员函数 和析构函数 可以用virtual关键字修饰。

3、virtual关键字的效果:

class GrandFather //祖父类

{

public:

GrandFather() {} //构造函数

virtual void fun() //虚函数声明定义

{

cout << "GrandFather call function!" << endl;

}

};

class Father : public GrandFather//父类,公有继承祖父类

{

public:

Father() {} //构造函数

void fun() //fun函数声明定义

{

cout << "Father call function!" << endl;

}

};

class Son : public Father //子类,公有继承父类

{

public:

Son() {} //构造函数

void fun() //fun函数声明定义

{

cout << "Son call function!" << endl;

}

};

void print(GrandFather* father) //输出函数 ,祖父类形参

{

father->fun(); //调用fun函数

}

int _tmain(int argc, _TCHAR* argv[])

{

Father * pfather = new Son;//建立一个父类的指针让它指向子类

pfather->fun();

GrandFather * pgfather = new Father;

print(pgfather); //祖父类指针变量

return 0; }

4、virtual的继承性:

只要基函数定义了virtual,继承类的该函数也就具有virtual属性;即 GrandFather, Father ,Son同时定义virtual void fun()与GrandFather一个定义virtual void fun效果是一样的。

(7)c语言虚函数扩展阅读

vitual关键字的用途:

1、vitual基类

在多重继承中,从派生类到基类存在多条路线时(多个继承脉络或者途径),一个这种派生类的对象实例化将包含多个基类对象,浪费资源且调用混乱的现象出现。

因此引入了vitual baseclass,来在运行阶段克服多个基类对象的产生。这个vitual是在运行阶段保证对象唯一性的。

2.vitual函数

虚函数的出现,是由于基类的指针可以执行派生类,因此引出了不便,引入vitual函数,来告诉编译器,出现这种情况时,在运行时动态链接进行处理。

3.vitual在纯虚函数中使用

纯虚函数完全是为了继承体系的完整,也是集成vitual函数的作用而产生的。代表了编译器阶段对象的绑定,将调用选择转移到运行时动态绑定。

综上:vitual关键的引入,可以理解为阻止编译阶段的静态绑定,将绑定(虚函数)和约束工作(虚基类)转移到运行时动态处理。


参考资料:网络——virtual

‘捌’ C语言问题虚函数的问题

虚函数的作用是可以通过基类的指针或者引用调到派生类的这个函数。

你上面的代码是演示虚函数的作用,不用去简便他。

你可以把这个程序中的virtual全部删除掉,然后再运行程序,观察一下两次结果的不一样,估计你就能理解虚函数的作用了。

‘玖’ C语言中什么函数不能声明为虚函数

c语言是面向过程的语言,c++是面向对象的

‘拾’ 在C++中,什么是运算符重载什么是虚函数

多态性是指用一个名字定义不同的函数,这函数执行不同但又类似的操作,从而实现“一个接口,多种方法”。

多态性的实现与静态联编、动态联编有关。静态联编支持的多态性称为编译时的多态性,也称静态多态性,它是通过函数重载和运算符重载实现的。动态联编支持的多态性称为运行时的多态性,也称动态多态性,它是通过继承和虚函数实现的。

2.函数重载

函数重载的意义在于他能用同一个名字访问一组相关的函数。

在类中普通成员函数和构造函数都可以重载,特别是构造函数的重载(他提供了多种初使化方式)给用户更大的灵活性。在基类和派生类的函数重载有两种情况:一种是参数有所差别的重载。另一种是参数没有差别的重载,只是他们属于不同的类。

可以用以下两种方法来区分这两种函数:用对象名加以区分;使用“类名::”加以区分。

3.运算符重载称动态多态性,他是通过继承和虚函数实现的。

运算符重载通过创建运算符函数operator@()来实现。运算符函数定义了重载的运算符将要进行的操作,这种操作通常作用在一个类上。这样,在编译时遇到名为operator@的运算符函数(@表示所要重载的运算符),就检查传递给函数的参数的类型。

重载运算符与预定义运算符的使用方法完全相同,它不能改变原有运算符的参数个数(单目或双目),也不能改变原有的优先级的结合性。用户不能定义新的运算符,只能从C++已有的运算符中选择一个恰当的运算符重载。

3.1成员运算符函数

运算符函数可以定义为它将要操作的类的成员(称为成员运算符函数),也可以定义为非类的成员,但是非成员的运算符函数大多是类的友元函数(称为友元运算符函数)。

成员运算符函数在类中的声明格式为:

class X{
//……
type operator@(参数表);
};

其中type为函数的返回类型,@为所要重载的运算符符号,X是重载此运算符的类名,参数表中罗列的是该运算符所需要的操作数。

成员运算符函数定义的形式一般为:

type X::operator@(参数表)

//函数体}

其符号的含义与声明时相同。

在成员运算符函数的参数表中,若运算符是单目的,则参数表为空,此时当前对象作为运算符的一个操作数,通过this指针隐含地传递给函数的;若运算符是双目的,则参数表中有一个操作数,它作为运算符的右操作参数,此时当前对象做为运算符的左操作数,它是this指针隐含地传递给函数的。总之成员运算符函数operator@所需要的一个操作数是由对象通过this指针隐含传递。

3.2友元运算符函数

在C++中可以把运算符函数定义成某个类的友元函数,称为友元运算符函数。

友元运算符函数在类的内部声明格式如下:

friend type operator@(参数表)
定义友元运算符函数格式如下:
type operator@(参数表)
{ //函数体
}

与成员运算符函数不同,友元运算符函数是不属于任何类对象的,它没有this指针。若重载的是双目运算符,则参数表中有两个操作数;若重载的是单目运算符,则参数表中只有一个操作数。 不能用友元函数重载的运算符是=、()、[]、-〉,其余的运算符都可以使用友元函数来实现重载。

运算符函数调用形式如下表:

运算符函数调用形式

习惯形式 友元运算符函数调用形式 成员运算符函数调用形式

a+b operator(a,b) a.operator+(b)
-a operator-(a) a.operator-()
a++ operator++(a,0) a.operator++(0)

4.赋值运算符

继承C语言,用户自定义的类和结构都要能进行赋值运算。而数组名不能赋值,数组名实质上是一个常量指针。

对于任何类,C++提供了默认的赋值运算符。一般地,默认的赋值运算符重载是能够胜任工作的。当类中有指针类型时,需要自定义赋值运算符函数。一般其函数体包含两部分:

1)与析构函数类似,取消对象已经占有的资源;

2)与构造函数类似,在其中分配新的资源。

类的赋值运算符重载“=”只能重载为成员函数,不能重载为友元函数。

重载后的运算符函数operator=()不能被继承。

拷贝构造函数和赋值运算符重载的区别:声明和定义方式不同;调用方式不同。

5.虚函数

虚函数是重载的另一种表现形式,允许虚函数调用与函数体之间的联系在运行时才建立。
定义:虚函数就是在基类中被关键字virtual说明,并在派生类中重新定义的函数,在派生类中重新定义时,其函数原形包括返回类型,函数名,参数个数与参数类型的顺序,都必须与基类中的原形必须相同。
构造函数不能是虚函数,但析构函数可以是虚函数。

虚函数与重载函数的关系:当普通的函数重载时,其函数的参数或参数类型必须有所不同,函数的返回类型也可不同;在派生类中,重新定义虚函数时要求函数名、返回类型、参数个数、参数的类型和顺序与基类中的函数原形完全相同;若仅仅返回类型不同,其余均相同,系统会给出错误信息。虚函数重载时若仅仅函数名相同,而参数的个数、类型或顺序不同系统将它作为普遍函数重载,虚函数的特征将会丢失。
多重继承与虚函数:多重继承可视为多个单继承的组合。

6.纯虚函数和抽象类

纯虚函数:是一个在基类中说明的虚函数,他在该基类中没有定义,但要求在它的派生类中定义自己的版本,或重新说明为纯虚函数。

纯虚函数的一般形式:virtual type func_name(参数表)=0(type是函数的返回类型,func_name是函数名)。

抽象类:一个类至少有一个纯虚函数的类。抽象类提供了处理各种不同派生类的统一接口,将实现的责任交给了派生类。

另外,站长团上有产品团购,便宜有保证

热点内容
apriori算法改进 发布:2024-05-04 19:24:08 浏览:44
为什么我要自己写脚本 发布:2024-05-04 19:19:13 浏览:182
手机照片重命名为什么还有文件夹 发布:2024-05-04 19:19:06 浏览:308
安卓手机为什么连不上iphone的热点 发布:2024-05-04 19:18:58 浏览:208
海量小文件存储ftp 发布:2024-05-04 19:13:21 浏览:273
真我手机如何解除手机密码 发布:2024-05-04 18:24:44 浏览:708
数据库嵌套 发布:2024-05-04 18:24:29 浏览:146
豌豆荚源码 发布:2024-05-04 18:10:54 浏览:117
苹果消息的声音安卓怎么弄 发布:2024-05-04 18:06:23 浏览:555
减配配置有哪些 发布:2024-05-04 18:04:58 浏览:963