为什么cy1是top0

问题描述:top1和0是什么意思 这篇文章主要介绍了一个有趣的事情,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获,下面让小编带着大家一起了解一下。

深交top0和top1哪个更重要

为什么cy1是top0的相关图片

top0和百万年薪出自不同的梗,当时top1带恶人说亚洲他最想做队友的就是cy1,于是说他是top0。

top0是略带嘲讽意味的。

top1是排名第一、第一名的意思。

top,是英文单词,作名词时,指顶部、山顶、头顶。作形容词时,指最高的、顶上的、头等的、最大的。作动词时,指形成顶部、达到…的顶端、处于…的最前头等意思。 当前多用于一些标题或者是排名,如:歌曲top就是歌曲排行榜,电影top就是电影排行。

在C++中用虚函数的作用是什么?为什么要用到虚函数的相关图片

在C++中用虚函数的作用是什么?为什么要用到虚函数

top0更重要。

TOP0是中国csgo职业选手summer(cy1)的梗,指的是地位较高选手,深交top1是第一名的意思,但是TOP0比第一名地位更加高,TOP0更厉害,也就更重要。

TOP是指某一类事物前几或者前一百或前任一数字的排名。排行榜是对某一相关同类事物的客观实力的反映,带有相互之间的比较性质。

在C++中用虚函数的作用是什么?为什么要用到虚函数的相关图片

在C++中用虚函数的作用是什么?为什么要用到虚函数

虚函数联系到多态,多态联系到继承。所以本文中都是在继承层次上做文章。没了继承,什么都没得谈。

下面是对C++的虚函数这玩意儿的理解。

一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你就应该从这里开始)

简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。下面来看一段简单的代码。

class A{

public:

void print(){ cout<<”This is A”<<endl;}。

};

class B:public A{。

public:

void print(){ cout<<”This is B”<<endl;}。

};

int main(){ //为了在以后便于区分,我这段main()代码叫做main1。

A a;

B b;

a.print();

b.print();

通过class A和class B的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,输出的结果也是我们预料中的,分别是This is A和This is B。但这是否真正做到了多态性呢?No,多态还有个关键之处就是一切用指向基类的指针或引用来操作对象。那现在就把main()处的代码改一改。

int main(){ //main2。

A a;

B b;

A* p1=&a;

A* p2=&b;

p1->print();。

p2->print();。

运行一下看看结果,哟呵,蓦然回首,结果却是两个This is A。问题来了,p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数。

class A{

public:

virtual void print(){ cout<<”This is A”<<endl;} //现在成了虚函数了。

};

class B:public A{。

public:

void print(){ cout<<”This is B”<<endl;} //这里需要在前面加上关键字virtual吗?

};

毫无疑问,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以,class B的print()也成了虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了。

现在重新运行main2的代码,这样输出的结果就是This is A和This is B了。

现在来消化一下,我作个简单的总结,指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。

二, 虚函数是如何做到的(如果你没有看过《Inside The C++ Object Model》这本书,但又急切想知道,那你就应该从这里开始)

虚函数是如何做到因对象的不同而调用其相应的函数的呢?现在我们就来剖析虚函数。我们先定义两个类。

class A{ //虚函数示例代码。

public:

virtual void fun(){cout<<1<<endl;}。

virtual void fun2(){cout<<2<<endl;}。

};

class B:public A{。

public:

void fun(){cout<<3<<endl;}。

void fun2(){cout<<4<<endl;}。

};

由于这两个类中有虚函数存在,所以编译器就会为他们两个分别插入一段你不知道的数据,并为他们分别创建一个表。那段数据叫做vptr指针,指向那个表。那个表叫做vtbl,每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址,我们可以把vtbl形象地看成一个数组,这个数组的每个元素存放的就是虚函数的地址,请看图。

通过上图,可以看到这两个vtbl分别为class A和class B服务。现在有了这个模型之后,我们来分析下面的代码。

A *p=new A;

p->fun();

毫无疑问,调用了A::fun(),但是A::fun()是如何被调用的呢?它像普通函数那样直接跳转到函数的代码处吗?No,其实是这样的,首先是取出vptr的值,这个值就是vtbl的地址,再根据这个值来到vtbl这里,由于调用的函数A::fun()是第一个虚函数,所以取出vtbl第一个slot里的值,这个值就是A::fun()的地址了,最后调用这个函数。现在我们可以看出来了,只要vptr不同,指向的vtbl就不同,而不同的vtbl里装着对应类的虚函数地址,所以这样虚函数就可以完成它的任务。

而对于class A和class B来说,他们的vptr指针存放在何处呢?其实这个指针就放在他们各自的实例对象里。由于class A和class B都没有数据成员,所以他们的实例对象里就只有一个vptr指针。通过上面的分析,现在我们来实作一段代码,来描述这个带有虚函数的类的简单模型。

#include<iostream>。

using namespace std;。

//将上面“虚函数示例代码”添加在这里。

int main(){

void (*fun)(A*);。

A *p=new B;

long lVptrAddr;。

memcpy(&lVptrAddr,p,4);。

memcpy(&fun,reinterpret_cast<long*>(lVptrAddr),4);。

fun(p);

delete p;

system("pause");。

用VC或Dev-C++编译运行一下,看看结果是不是输出3,如果不是,那么太阳明天肯定是从西边出来。现在一步一步开始分析。

void (*fun)(A*); 这段定义了一个函数指针名字叫做fun,而且有一个A*类型的参数,这个函数指针待会儿用来保存从vtbl里取出的函数地址。

A* p=new B; 这个我不太了解,算了,不解释这个了。

long lVptrAddr; 这个long类型的变量待会儿用来保存vptr的值。

memcpy(&lVptrAddr,p,4); 前面说了,他们的实例对象里只有vptr指针,所以我们就放心大胆地把p所指的4bytes内存里的东西复制到lVptrAddr中,所以复制出来的4bytes内容就是vptr的值,即vtbl的地址。

现在有了vtbl的地址了,那么我们现在就取出vtbl第一个slot里的内容。

memcpy(&fun,reinterpret_cast<long*>(lVptrAddr),4); 取出vtbl第一个slot里的内容,并存放在函数指针fun里。需要注意的是lVptrAddr里面是vtbl的地址,但lVptrAddr不是指针,所以我们要把它先转变成指针类型。

fun(p); 这里就调用了刚才取出的函数地址里的函数,也就是调用了B::fun()这个函数,也许你发现了为什么会有参数p,其实类成员函数调用时,会有个this指针,这个p就是那个this指针,只是在一般的调用中编译器自动帮你处理了而已,而在这里则需要自己处理。

delete p;和system("pause"); 这个我不太了解,算了,不解释这个了。

如果调用B::fun2()怎么办?那就取出vtbl的第二个slot里的值就行了。

memcpy(&fun,reinterpret_cast<long*>(lVptrAddr+4),4); 为什么是加4呢?因为一个指针的长度是4bytes,所以加4。或者memcpy(&fun,reinterpret_cast<long*>(lVptrAddr)+1,4); 这更符合数组的用法,因为lVptrAddr被转成了long*型别,所以+1就是往后移sizeof(long)的长度。

三, 以一段代码开始

#include<iostream>。

using namespace std;。

class A{ //虚函数示例代码2。

public:

virtual void fun(){ cout<<"A::fun"<<endl;}。

virtual void fun2(){cout<<"A::fun2"<<endl;}。

};

class B:public A{。

public:

void fun(){ cout<<"B::fun"<<endl;}。

void fun2(){ cout<<"B::fun2"<<endl;}。

}; //end//虚函数示例代码2。

int main(){

void (A::*fun)(); //定义一个函数指针。

A *p=new B;

fun=&A::fun;。

(p->*fun)();。

fun = &A::fun2;。

(p->*fun)();。

delete p;

system("pause");。

你能估算出输出结果吗?如果你估算出的结果是A::fun和A::fun2,呵呵,恭喜恭喜,你中圈套了。其实真正的结果是B::fun和B::fun2,如果你想不通就接着往下看。给个提示,&A::fun和&A::fun2是真正获得了虚函数的地址吗?

首先我们回到第二部分,通过段实作代码,得到一个“通用”的获得虚函数地址的方法。

#include<iostream>。

using namespace std;。

//将上面“虚函数示例代码2”添加在这里。

void CallVirtualFun(void* pThis,int index=0){。

void (*funptr)(void*);。

long lVptrAddr;。

memcpy(&lVptrAddr,pThis,4);。

memcpy(&funptr,reinterpret_cast<long*>(lVptrAddr)+index,4);。

funptr(pThis); //调用。

int main(){

A* p=new B;

CallVirtualFun(p); //调用虚函数p->fun()。

CallVirtualFun(p,1);//调用虚函数p->fun2()。

system("pause");。

现在我们拥有一个“通用”的CallVirtualFun方法。

这个通用方法和第三部分开始处的代码有何联系呢?联系很大。由于A::fun()和A::fun2()是虚函数,所以&A::fun和&A::fun2获得的不是函数的地址,而是一段间接获得虚函数地址的一段代码的地址,我们形象地把这段代码看作那段CallVirtualFun。编译器在编译时,会提供类似于CallVirtualFun这样的代码,当你调用虚函数时,其实就是先调用的那段类似CallVirtualFun的代码,通过这段代码,获得虚函数地址后,最后调用虚函数,这样就真正保证了多态性。同时大家都说虚函数的效率低,其原因就是,在调用虚函数之前,还调用了获得虚函数地址的代码。

最后的说明:本文的代码可以用VC6和Dev-C++4.9.8.0通过编译,且运行无问题。其他的编译器小弟不敢保证。其中,里面的类比方法只能看成模型,因为不同的编译器的低层实现是不同的。例如this指针,Dev-C++的gcc就是通过压栈,当作参数传递,而VC的编译器则通过取出地址保存在ecx中。所以这些类比方法不能当作具体实现。

EDA时钟设计求讲解校时部分的相关图片

EDA时钟设计求讲解校时部分

虚函数:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

众所周知,在同一类中是不能定义两个名字相同、参数个数和类型都相同的函数的,否则就是“重复定义”。但是在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数和类型都相同而功能不同的函数。例如在例12.1(具体代码请查看:C++多态性的一个典型例子)

程序中,在Circle类中定义了 。

area函数,在Circle类的派生类Cylinder中也定义了一个area函数。这两个函数不仅名字相同,而且参数个数相同(均为0),但功能不。

同,函数体是不同的。前者的作用是求圆面积,后者的作用是求圆柱体的表面积。这是合法的,因为它们不在同一个类中。

编译系统按照同名覆盖的原则决定调用的对象。在例12.1程序中用cy1.area( ) 。

调用的是派生类Cylinder中的成员函数area。如果想调用cy1 中的直接基类Circle的area函数,应当表示为 。

cy1.Circle::area()。用这种方法来区分两个同名的函数。但是这样做 很不方便。

人们提出这样的设想,能否用同一个调用形式,既能调用派生类又能调用基类的同名函数。在程序中不是通过不同的对象名去调用不同派生层次中的同名函数,而是通过指针调用它们。例如,用同一个语句“pt->display( );”可以调用不同派生层次中的display函数,只需在调用前给指针变量 pt 赋以不同的值(使之指向不同的类对象)即可。

打个比方,你要去某一地方办事,如果乘坐公交车,必须事先确定目的地,然后乘坐能够到达目的地的公交车线路。如果改为乘出租车,就简单多了,不必查行车路。

线,因为出租车什么地方都能去,只要在上车后临时告诉司机要到哪里即可。如果想访问多个目的地,只要在到达一个目的地后再告诉司机下一个目的地即可,显。

然,“打的”要比乘公交车 方便。无论到什么地方去都可以乘同—辆出租车。这就是通过同一种形式能达到不同目的的例子。

C++中的虚函数就是用来解决这个问题的。虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。

为什么我的程序出现这样的错误?下面是程序代码,错误信息在最底下No such file or directory

1.Topclock(元件例化 顶层文件) Library ieee; Use ieee.std_logic_1164.all; Use ieee.std_logic_arith.all; Use ieee.std_logic_unsigned.all; ...。

原文地址:http://www.qianchusai.com/%E4%B8%BA%E4%BB%80%E4%B9%88cy1%E6%98%AFtop0.html

predicate的定义,predicate和predicator区别

predicate的定义,predicate和predicator区别

三只小猪还有一只狼的故事,三只小猪还有一只狼的故事内容

三只小猪还有一只狼的故事,三只小猪还有一只狼的故事内容

世话烧-90,世话烧制服少女

世话烧-90,世话烧制服少女

python图形怎么保存,python画出来的图怎么保存

python图形怎么保存,python画出来的图怎么保存

tp路由器刷梅林教程,tplink路由器可以刷梅林吗

tp路由器刷梅林教程,tplink路由器可以刷梅林吗

初源堂-20,初源堂道家养生会馆

初源堂-20,初源堂道家养生会馆

开多次方根号的方法和步骤,怎么开多次方根号

开多次方根号的方法和步骤,怎么开多次方根号

一年级看图写话五要素,一年级看图写话五要素写好的字

一年级看图写话五要素,一年级看图写话五要素写好的字

cc/小丑背景哭泣笑绝望,最后小丑竟然是我

cc/小丑背景哭泣笑绝望,最后小丑竟然是我

lunar,lunar new year是什么意思

lunar,lunar new year是什么意思

知网能在线阅读吗 ai辅写疑似度怎么查 知网ai工具在哪里 知网阅读工具 学校查重会查ai辅写吗 知网论文可以免费在线阅读吗 知网文献下载工具 文章检测ai辅写疑似度 中国知网在线教学 中国知网能在线阅读 知网在线阅读收费吗 知网阅读器推荐 中国知网在线教育直播平台 中国知网在线阅读付费吗 知网人工客服在线时间 中国知网在线平台 知网研学平台在线翻译工具 PaperBERT移除AI痕迹工具 中国知网是免费在线阅读吗 什么是ai辅写 知网论文免费免费在线阅读 觅知网ppt在线编辑 知网的在线阅读要多少钱 小发猫文章同义句转换器 知网免费在线查文献 跟知网配套的阅读器 论文在线阅读中国知网知网研学平台在线翻译工具有什么 知网在线登录入口 知网在线阅读方式