00:00
从逻辑上看,感觉这个合约应该没有问题,对吧,看起来好像无懈可击,就是这个逻辑很清晰,也很好理解,只不过就是他一开始呃,写了一个那个就是写死的一个数嘛,对吧,然后之后他让大家猜,然后猜的时候呢,你要先传进来一个数。他就先创建一个盖的一个结构体,然后呢,把把你当前的这个地址给到这个player里边,把传进来的这个数量给到这个number里边来,然后还还加入到历史里面去。然后最后就是如果判断这两个你的给定的这个数等于预先设置好的lucky number,那么就直接给你发B。看起来没什么毛病啊,哪一步有问题,我们查一下这个history哦,大家看过,刚才这应该是。
01:04
他应该是第零个位置,应该有这个两个人来参加对吧?哎,我刚才应该是点了一次还是两次,为什么这个是NUMBER0呢?好,我们再参加一次,盖一下。好,看一下这个里边这还是一样的是吧?我们给一个一看一下能拿到东西吗?是全是零是吧?直接报错哦,高版本直接报错了,是0.5.0是吗?哦,0.5.0,呃,所以看起来0.5.0要把现在的很多坑要填上啊,大家觉得可能是哪里有问题,盖有问题是吧?那是哪哪一步好好就是11行是吗?第十行,哎,大家看第十行这里是有一个warning的,对吧?正常来讲这里肯定是能看到这个warning的,大家看这个warning说的是什么?
02:12
跟我们刚才看到那个war好像啊,就咱们之前看到那个,就是写一个data,会把会把A改变的那一个warning很像,对吧,他说的是什么。这个变量被声明成了一个storage类型的指针。所以这里看起来又是同样的一个问题,对吧,那上只不过上一次我们这里定义的是一个变常数组,定义了之后没给赋值,是一个storage类型的指针,这里呢,它改成了一个结构体,一个结构体定义了之后没赋值,这个new GA就变成了一个指针,Story类型的指针,那所以大家现在能想象得到之后我们的赋值会发生什么事情。
03:01
如果说这是一个storage类型的指针。在没有负值的情况下,它就会指向。会指向哪里?会指向整个我们存储空间的最初的那一部分,对吧,所以最初的这一部分存的是什么?对,是我们的lucky number。所以这个lucky number的这个状态是会随着这个指针,如果要是改写它当前的内容的话。就会把我们lucky number改掉对不对?所以他的整个的玄机就在这里,我们为了验证我们的想法,可以自己把这个lucky number打出来,当然就是说如果这是一个真实的东西,我们肯定不会把它打出来。在这里我们要做测试,所以我们试一下把lucky number我们看一下啊,看一下lucky number52。
04:02
好,那我就猜52。猜一下。嗯。咱们再看一下lucky number是什么?Lucky number已经变成了一个我们根本不认识的一个东西。所以大家知道这是为什么了是吧,就是因为。在一开始他定义这个时候,故意定义一个。跟我们,呃,就是他故意定义一个storage类型的指针。而且不给他付出值,这个时候它就会直接指到我们整个定义的最初的那一部分,也就是指向我们的lucky number,在后面看起来像是我们很正常的一个操作,再去把当前的信息记录下来的一个过程中,其实把new guest的内容写写上信息的时候,就已经覆盖掉了我们lucky number的内容。
05:04
它覆盖的时候用的就是我们me send和这个number的这个信息就会写到这个里面,所以我们会看到一个很奇怪的东西,因为和和我们的地址有关,对吧。这是这是一个U的数,我们地址本来是一个字符,所以不可能一一对应上,所以大家可以看到就是在这个过程当中。他就掩盖了自己的这个真实目的,据说这是一个真实布在以太坊上的一个合约,但是我我是没有找到啊,据说是真实的。而且他发布出来之后,有人就管他说就是就是类似于一个钓鱼,或者是大家可以看到我在PPT里边给的这个名字叫什么叫叫honey pot。这是一个蜜罐,实际上是一个陷阱。对,就是他故意把这个布上来之后,大家有些有些人可能还读不懂吧,但偏偏就是我们有些搞技术的人还还读得懂,一看,诶,你这个合约这么简单,你都写死了,你以为我不识数啊?你都写死了,Lucky number就是52,这还用猜吗?那我发一个52,你肯定得给我钱啊。
06:14
于是就有很多人去尝试,结果就中了这个圈套,就会发现就是它其实是利用了一个就是solidity,整个语言设计上大家可以说是一个漏洞也好,或者怎么样也好,所以大家看到如果要用0.5.0的编译器的话,就会把这个直接当成就是报错编译不通过,那这样的话安全性就又提高了,对吧?但是至少在之前的版本,我们现在正式发布的呃,0.4.25的版本里边,这个还是只是warning是不报错的。所以在这个合约里边就可以这么玩。别人看到以后,以为可以逮便宜,直接发一个以太过来,结果发现发多少丢多少,根本提不提不出来,所以而且大家可以看到就是,呃,有有些同学可能想,那如果要这样的话,我第一次发完了之后,第二次是不是就可以提出来了,有没有这种可能?
07:15
还是有的是吧,有有有什么办法能把B能提出来。哦,就是如果我们能访问到这个new的话,那就可以了,但是这不可能啊,这是它是一个局部变量,我们访问不到。所以其实大家可以看到,就是假如说我们现在一直能把lucky number打印出来的话,假如这是一个public类型的话,其实我们是有办法去去猜的,我们下次猜什么。大家可以试一试啊,我下次就猜这个。
08:03
呃,我发十个试试,呃,对这样啊,我先我先发一次试试,咱们先不发钱对吧?好,我就发这个数试一下,大家看下面这个数会不会变。好像没变对吧,为什么呢?位置,对,这个stra它占的位置是固定的,对吧?第一个占的位置就是player,所以如果我们知道这个number是多少的话,我们下次还用同一个账户,就是me send一定得一样,对吧?我们还用同一个账户去发这个交易,然后猜就猜这个数,那么覆盖上去之后会发现跟之前的那个就应该是一样的。所以在这种情况下,咱们试一下啊,我现在有53个,我发20个。然后这个对吧。
09:01
有报错了。The transaction has been reverted to the initial state。好,Should be payable if you send value就是。如果说我要。Me sun。我们的message.sender肯定是payable的,对吧?这是我们自己的用户地址,用户地址一定是一个payable的地址,那它为什么会提示说就是constructor必须是一个payable呢?好,我们看一下这里应该是对这个是配这个是没有问题的,我们可以向他付钱。
10:00
但是我们自己的地址好像它。不能够付钱是吧,这个我不知道是不是我们这个JSVM的一个问题啊,成功了好像成功了是吗?哦呃,现在不知道写的是什么啊,其实我们有一个方法,其实就是把这个找出来之后,然后再去复制过去。正常来讲是可以成功的,你们找一下这个这个问题是怎么回事啊。我们看一下。From mes sand。我们看到这是从我们自己的这个账户发出来的一笔交易,发到C的点盖方法里边,这是这是我们这个账户,这是合约的地址。然后下面哈希。
11:07
呃,这个里边如果正常来讲的话。我们向这个,我们并没有向这个合约转币,它其实只要调这个方法,它是payable的就可以,但是这个时候你你是说在这个里边加上回退函数是吗?合约就是给合约添加回退函数是吗?但正常来讲的话,就是他如果往出转币的话,跟他是不是回退函数是没关系的,就就是他是他的回退函数是不是payable,你那边可以是吗?好,我们试一下吧,Function加一个he的。回退函数好看一下啊。删掉来把它部署一下。先看一下lucky number。
12:04
好,我们先随便猜一个啊,还是猜52万,那就。Lucky number变成了这个。诶,好像看一下还是报错,呃,你给了value吗?给了而且拿回来了是吧?哎,我这边看起来是有点问题啊,你那边用到的是呃,GS的VM吗?对呃,这个是有点奇怪啊,这个问题大家可以在自己把版本调到0.44,哦,我我这个不敢随便调啊,因为我编译器完了之后下下不到了,然你说调这个是吧,这个其实没关系,这是跟我们的编译器有关,只要我们就是这个只是限定我们编译器的版本对吧?模对一模一样,这个有时候可能会有一点问题啊,大家可以在自己本地先试一下啊。
13:11
然后我也看一下这个问题到底是为什么。正常来讲的话,大家可以看到,就是假如说我们把这个,因为刚才呃,已经试验成功了是吧,把它提出来了是吧,对,所以如果我们给定的这一个数,大家可以看到,因为他每次占用我们这个lucky number位置的都是message点三,所以其实理论上我们有可能只要我们能知道它现在到底是多少的话,我们打把它复制过来,下一次我们还用同一个mes send去发,那按道理。就应该是一样的,对吧,所以大家已经可以看到,我现在这个回退其实跟这个逻辑是没关系的,至少他已经走到transfer这里了,只不过是因为我们其他的一些一些状况好像导致了它的这个回退。我们用。
14:05
又不行了是吗?哎,这个很很奇怪的状态啊,我换一个账户试一下吧。Deploy一下。Number。Lucky number,好。哎,我看如果说我直接猜52的时候,这个给难道也不行吗。那这个是可以的啊,直接扣了。所以他就是他在真正走到这个相等的时候,如果要给我转的时候,他发现有问题,所以说我们这个账户的问题啊,账户怎么会这样。好像是口。商务局前迫。
15:00
呃,是钱扣完了会有问题是吗?呃,但是我们这个里面是有钱的呀,是先去的,哦对对对对对,有道理,对,这个说的有道理,因为我们没有去查我们这个合约账户里面到底有多少钱,对吧,那前面我试了好几次,有可能已经把它提完了,呃,当然这只是有可能啊,我不确定。猜猜几个错几个好,我们这个数小一点,我们试一下吧,我现在是89.99个对吧,我猜三个。看看能不能猜对啊。Guess,哎,大家看到上面好像真的多了对吧,真的多了是不是,呃,不不是,不是双倍吗?因为我给他发了三个呀,对吧,我发了三个,然后他返还六个的话,我应该加三个对吧?对,所以说这个是对的对吧?哎,大家看我终于从这个这么神奇的一个陷阱里边提出了B,但是事实上大家看,如果这个他没有加这个public的话,我们能提出来吗?那我们就永远不知道这个数对不对,如果不加public的话,我们就拿不到这个状态。
16:25
那我们真的就提不出来。所以这个合约就是确实还是很坑的,就是大家如果要是以后能遇到这样的,但当然教练是大家不要去坑人啊,就是呃,如果看到类似的合约可以注意一下,就是他往往都会利用了我们给大家说的这个storage类型的指针,如果一开始没有被数值,它会指向最初的位置。很多这样的情况,都是这样的一个应用啊,当然这个这就是属于就是有人动了歪脑筋,他专门就拿这个来坑人,而且就是让大家一看似乎还觉得,诶你这就跟送钱一样,结果一不小心就给他送钱了,所以。
17:12
而且这个其实,呃,从理论上来讲,大家觉得这个东西是不是还可以再做一些改进。假如说是骗人的话啊,咱们也学着去骗人的话,是不是还可以再做一些改进。刚才我们说如果我这里给一个public给到给到这里的话,那我们如果要是每次都能看到这个lucky number,我前一次。我我赔了一点钱发进去了,下一次我还输这个就对了,对吧。那有没有可能我们设计这个合约的时候设计的让别人连这个空子都钻不了?自己感觉,嗯。哎,大家想想,我们刚才能把这个能碰对的原因是因为我们前前后两次调用的时候,它这个数不会变了,对吧?那这个数是被谁改的呢?改成这个样子的是me th对吧?发送者的这个地址给到了player,所以就改成了跟发送者相关的一个东西,所以我们每次发送者一样的话,那这个肯定就不变。
18:26
那大家想一想,我这样改,每次都改发送者,主要是因为我们定义盖的时候先定义了player对不对?我调一下顺序会怎么样呢?我调一下顺序,把number放在前面。大家可以猜测一下这样会变成什么样。我删掉,重新部署一下deploy。这样会怎么样?我们先看一下啊,Lucky number52我先去。
19:03
哦,如果说这样的话。其实这是会用guess的这个数目去覆盖它,对吧,那这个其实就没有意义了,那别人猜对之后其实是可以对的啊,我们试一下。呃,这个这个我们首先应该部署的时候,得得给他一点钱啊,要不然他提不出来50吧。Deploy。嗯。哦,对,这个就是他没有没有这,诶我们定义了回头函数的。哦,是他的那个constructor,必须是必须得是payable才可以,那我们还是算了。我们多猜两次好了。给一个50。Lucky number。我给个给个五吧,盖一下。
20:00
大家说现哎,怎么又挂了,Constructor should be payable if you send value,还是报这个错啊,这个错,很奇怪的样子。大家是觉得他没有那么多钱是吧。哦,但大家知道这个是什么问,呃,就是这个问题是什么吗?我把这两个调了之后出现的状况。其实不是坑了别人,是把自己坑了。大家知道为什么会把自己坑了吗?我现在最前面的这个已经不是player了,而是猜的,就是别人猜的number对吧,所以我这里每次往里写的时候。
21:01
首先我们现在这个guess是会占用lucky number的位置,对吧是吧,Lucky number里面写,那guess往上写的第一个东西是什么呢。是number。所以这就变成了别人传什么进来,我们就把什么东西先先写进去。我传一个五进来,那首先就会把lucky number改成五,但哦,这个刚才因为那个回退了啊,所以这个我们看不到,我们这里还是加一个constructor吧的constructor,这样我们可以直接先给他给上钱,这个就好办一些。好,给一个这个。我们创建的时候就给他50。好,现在已经创建好了,这里边应该肯定有钱了,对吧,现在number是52,那我们假如猜五的话。
22:01
猜。大家看是不是给回来了。大家没注意是吧?那我们看看现在lucky number是几?Lucky number是五。我再猜一次,比方说78。我给十个猜十个啊,大家看上面的余额有没有变化啊猜。又加了十。现在lucky number是。是78。这就把自己坑了对吧,就说了人家输什么,你的lucky number就变成什么,所以每一次人家一定是对的,所以大家可以看到这个就是这么细小的一个变化,把这个number和player稍微换一个顺序,就由坑人变成了自坑。对,所以。大家感兴趣的话,可以就下来自己想一想,怎么样坑人,怎么样怎么样避免被坑,好这这是就是给大家讲的一个稍微比较有趣味性的一个蜜罐合约啊,就是这样的一个蜜罐合约,据说现在在以太坊上也也不少,尽管我是没有就是具体找到他们的地址,因为所有的这些合约只要发布之后,那相当于已经是不受人控制了,对吧。
23:25
所以就是只要这个地址里边真的是有以太的,那假如说你真的猜对的话,那他这个真的要给你转出来的,所以可能很多人看到这样的合约之后,就有点按耐不住自己激动的心情,想着终于找到一个比水龙头还好使的地方,呃,然后自己结果就对,结果就把自己的币转进去了,所以就是就是这样的一个一个状况啊好,我们讲过了这个稍微好玩一点的东西之后,下面就又是可能比较枯燥的一些东西了,来给大家讲一讲函数声明和类型。
24:05
那函数声明其实大家都已经比较熟悉了,我们都已经用了这么多次,主要它可以分成这几类,首先函数声明的时候,第一个就是我们的关键字function,然后后面是函数名称,加上小括号,里边加函数参数。后面的这一部分呢,我们可以统一把它归类成函数类型。具体还可以再分啊,就是大家看到这个public view,这其实两类东西等一下我们再说,最后面是它的返回类型,Returns,关键字后面是括号,里边返回数据的就是数据类型,所以大家可以看到就是我们平常用的时候都是这样的啊,而且我们之前都已经试过了,返回可以返回多多个数据对吧,返回多个参数,这都是跟我们其他的编程语言很接近的地方,在函数的。类型里边。
25:01
有这么几个概念,就是大家有时候可能可以看到不同的这种分类概念,在这里边,我觉得就是官网上它的这个定义应该还是比较比较准确的一个定义的,就是首先它定义了一个叫函数的值类型。这就是我们前面说的,函数本身也是可以作为一种值类型赋给一个变量的,那这样的一个值类型又分成哪哪几类呢?主要是两类,就是我们所说的内部函数还是外部函数,就是internal还是ex external。这两种类型有什么区别呢?内部函数internal的函数呢?就是只能在当前合约被调用,具体来说,Internal类型就是只可以是当前代码块,包括内部的库函数,还有继承的函数里边可以调它,因为他们就是不能在当前合约上下文外部被执行的。所以。
26:03
我们调用一个内部函数的方式是通过跳转到它的入口标签来进行的,当然当然这是底层的,这个就是编译器和EVM的处理方式啊,它的这样的一个操作呢,就相当于就像我们在当前的合约里面内部调用函数一样,所以这就是内部调用方式,给大家举一个例子吧,这个。好,我们这里已经有了一个C,我们还是用的简单一点吧,比方说C里边我们就int一个A,然后function。呃,我们随便定一个F啊。Public public,好,这是我们最简单的一个合约,我们什么都没做,就定义了一个合约,对吧,然后我们再来。
27:01
定义一个合约D。D在这里边大家可以看到,如果说我们D里边定义一个C的A,呃,我们直接定义方程啊,Function g吧,Public好,在里边我们去定义一个C的类型,建定义一个合约C的类型的一个变量,那就是要建建立一个合约的实那个我们的实例了,对吧?所以可以用new的方式去拗一个C,刚才我们其实已经做过这个操作了啊,然后接下来就可以做调用。那我们要调用它的F函数的时候,怎么调呢?对c.F对吧,这样调,这种调用方式是我们所说的,刚才所说的外部调用,由一个地址和一个函数签名组成,通过外部函数调用传递或者返回,这就是我们所说的外部调用,所以大家看啊,外部调用方式就是。
28:10
当前合约也可以用外部调用方式调用自己的函数,怎么调呢?就是this.f,我们其他的编呃编程语言里边经常用到的this,点自己的这个函数名称,这种方式在solid里边认为是用了外部调用的方式调用自己,那正常的调外部合约当然就要用外部合约的名称和自己和它的函数名来调用了,所以是a.F这样啊。好,所以这是一个外部调用的方式,那我们说这个能不能直接内部调用呢?我们直接直接F能行吗?不行,这里报错了对吧,就是声明的错误,没有声明过这个F。那我们怎么样就能在这里直接用内部调用的方式来调了呢?
29:06
嗯,我们怎么样可以调用。对,有些同学可能想到了,就是我们可以直接用一个继承,在so里边,所有的合约都是可以继承的,那继承的写法非常的简单,就是ec,这就是D继承了C。那同样继承之后,他就可以拥有C里边的所有的,就是他不需要再创建一个C的。呃,实例,然后直接就可以调用C里边的方法,所以这是。呃,D的这个用法就是继承之后的这个用法,那大家可以想到这里你是public的一个一个函数,对吧,那我如果要是private呢。
30:00
Private,大家觉得对private就不行了,这其实跟我们在其他的编程语言里面是一样的,对吧,就是你如果要是这个定义成私有的话,即使是继承了它的类,或者说我们这里的合约,那也是不能叫它的私有方法的。那这里如果要是定成别的一些情况,比方说我们不是说还有internal吗。Internal就可以了,对吧,内部调用,所以继承了他的这些合约,也是可以用内部调用的方式直接去访问,在这里其实已经可以,就是大家可能会会感觉到,就是假如说我这里去部署地的话,大家想我直接去部署地的话。会不会把C的代码也也部署上去?之前大家看,就假如说我们是一个C一个D的话,我们在这里部署啊,这里不行啊,我先注掉,假如一个C一个D的话,我们这里肯定是C和D分别部署的,对吧。
31:07
那假如说这里我们D是继承了C的话,当然这里也可以分别部署C和D,对吧,那大家想一想。之前我们不继承的时候,当然C跟D其实没什么关系,部署D就是DC,就是C,那现在我部署D的时候。会有C的代码在里面吗?对,大家想到了,肯定有,你如果没有C的代码的话,那这个F到哪去找呢?D的这个代码在里面,根本就没有F啊。所以我们在这里可以deploy一下D啊。Deploy。大家可以看到这里边是只有G的,但是事实上在D的deploy的过程当中,我们这里可能看不到啊,这个这里可能不是很明显,但是之后我们给大家介绍地的部署过程的时候,就会给大家讲解,就是他在编译的时候其实是会。
32:07
把自己里边所有的这些合约,就是他继承的那些代码都会编译出来的。而且就是说呃,当然部署的时候还是只部署他自己的那个呃,就是我们所说的那个字解码。不会不会去把C的思想录上去,但是就是说它里边是带了C里边的信息的,这些东西都是带在里边的,好OK,所以我们接下来就看这是函数声明啊,这个比较简单。然后在这里又有一个不一样的概念,叫做函数的可见性。这个和前面我们说的函数的值类型是容易搞混的两类,而且经常我们写的时候其实也是混在一起写的。大家要注意,就是说我们要搞清楚前面说的external还是还是eternal说的是在外部要去调用函数的方式,或者说是我们把这个函数赋给另外一个变量的时候,这个变量它所有的值类型,而这里我们所说的可见性呢?
33:22
指的就是说它到底是在哪里可以看到,在哪里可以调用,这说的是可见性,但这个多多少少还是有一点关系的,对吧?就是多多少少有一点关系,所以大家可以看到,有时候很多文章其实把这两者混在一起谈的,那它的可见性有哪几类呢?这个就是我们常说的public private external和eternal。对于状态变量,大家可以看到啊,它是不能设external的,所以它默认是eternal。所以对于状态变量而言,我们可以用一个public把它定义成一个就是可以公开可见的一个变量。事实上,我们定义public的时候,也不是说状态变量是public,而是给它定义了一个public同名的函数,对吧?
34:17
大家还记得吗?我们当时说对用public修饰一个一个变量的时候,就相当于给它定义了一个同名的函数,好,我们还是在这里写一下吧,有些同学好像有点忘记了啊,比如说这里contract c里边定义了一个unit a,如果我们这里加一个public a的话,它相当于什么呢?相当于我们下面定义了一个function a。一个参一个函数对吧,它是public类型的。而且这个定义是相当于它是view类型的,等一下我们再再说这个view啊。Return return,这里应该有returns,它是有返回类型的returns,一个unit。
35:10
然后A就相当于我们定义了这样一个函数。所以大家会看到,我们如果去访问public的状态变量的时候,其实是要用函数调用的方式去访问的,对吧?之前我们曾经见到过这种情况,就后面不能加方括号去访问一个公共的数组,就是public类型的数组,而是要用括号去访问。这就是我们这个为什么会有这样的一个类型,所以说对于状态变量而言,它其实没有所谓的可见性一说,它默认就是internal。那是,呃,为什么还会有public呢?那public事实上不是说他public了,而是加了这一个参数之后,我们自己让编辑器给它又又加了这么一个函数。
36:03
那我们这个函数是public类型,所以大家注意啊,就是在这里我们所谓的可见性其实只是函数的可见性有这么一些区分,对于状态变量而言,它默认就是internal。好,呃,那么我们接下来看一下,就是这几类的这个区分啊,就是ex ex explore呢就是所说的外部函数,外部函数呢就是合约接口的一部分,就是如果要是想让别的合约或者说就是作为这个类库来调用这个函数的话,其实很多就是用这种external的方式的。它意味着我们可以从其他合约和交易当中来调用这个合约的函数,那当一个外部的函数不能从内部调用。哎,一个一个函数啊,一个外部函数,它本身当然就不能从内部调用了,就是如果我们明确定义了external之后,它的它跟public不一样的,就是说public是谁都能掉。
37:05
而如果定义了external之后。他的意思是说只能从外部钓鱼,内部都不能访问,所以这就相当于是屏蔽开我们内部的一些对他的一些动作。就是说,呃,这里有一个用法的说明,就是当收到大量数据的时候,外部函数可能有时候会更有效一些,就是我们不要让内部状态的变化,内部的这种调用影响到我们的一些,就是外部的处理,那public我们很熟悉了,Public就是。完全公开,内部可以调用,外部也可以通过消息调用,对于啊,大家可以看到这里专门说了一句啊,对于public状态变量的话,那其实是会自动生成一个get函数的,就是刚才我们写的那个。还有就是internal internal的话我们刚才已经看了,就是只能是内部访问,从当前合约内部或者是继承了他的,就是从它派生出来的合约去访问internal是可以的。最后就是private private就是只有函数。
38:11
就是只有在当前定义的合约里边可以使用,不能在就是派生合约里面使用,这里又有一个可见性的例子,这个其实很简单了,我们就不去敲了吧,我们有一个合约C,然后定义了一个函数F,这个F呢,它是一个就是就是计算一个A加一,输入一个数,然后返回一个A加一,然后还有一个方式叫S,就是把。诶,这里是不是少了一个哦,Data定义在后面了,就是状态变量定义了一个data public data。然后三塔呢,就是输入一个数,把data的状状态改了。然后还定义了一个方程X。F呢?在这里他就要访问自己内部的状态变量,还有这个自己内部定义的函数了,那首先是贝塔等于三,这就是对状态变量的一个内部访问。
39:08
大家注意看啊,还可以用外部访问的方式,因为这是一个public的。用public定义的data对吧?所以这个data会在这个合约里边会自动生成一个data的get,就是我们所说的一个public的函数,就叫data的一个函数,所以我们可以用外部访问的方式去访问这个data,怎么访问呢?this.data括号就拿到了这个data塔的值,我们可以用这种方式去访问。呃,还有另外就是这个函数的调用了,那大家可以看到就是既然是在内部嘛,直接就是F就可以用内部访问的方式,好呃,好,这里还有一个带错误的一个东西啊,大家大家来一起分析一下吧,我我这里把这个答案都已经写出来了,我把这个删掉,大家其实这个都都不用删,大家应该。
40:08
单独一看就知道了啊好,我们看一看吧,看一下它问题出在哪,上面定义了一个C。然后呃,大家看里边有一堆东西啊,那个有一个data,这是一个哦,这个data是可以用private来来修饰的啊,它只不过是不能用external来修饰。就是可以用private来修饰,如果定义了private private的话,那肯定就是就是纯私有了,它继承的也不可以继承的合约也不可以去访问,对吧?这个我们都是从字面意义上就能就能看到,那么下面我们来看它定义的方式,F是一个private类型的。然后set data是一个public类型的。然后get public。还有一个compute是一个internal类型啊,我们主要就看它函数类型吧,主要这块就说这个事情。
41:06
那下面我们就看到有一个CD。D在调用函数,它的定义了一个方法叫read data。在里边呢,他就创建了new了一个C。然后它定义了一个local的一个unit叫,就叫local等于c.F7,这个调用方法是什么?就是我们所说的外部访问对吧?对外部访问c.F7这个可以吗?对,大家看到了,F是一个私有的方式,你这种访问,那就是用外部访问去访问一个私有的函数肯定是不行的,所以这个肯定是会报错啊,然后下面我们再看c.set贝塔三这个可以吗?啊,这个肯定可以,因为这个这是public的嘛,我直接用外部访问肯定是可以访问的。
42:02
c.get log等于可以吗?呃,应该也没问题,Get data也是public对吧?只要是public类型的,那内部访问和外部访问其实都是可以的,呃,当然了,这个因为它没有继承关系,所以如果要用内部访问,那肯定也不行,对吧?所以肯定是要用外部访问的形式,最后local等于c.compute35,这个可以吗?这个可以吗?好,那我们还是来验证一下吧,大家对这个有点疑惑啊,这个我就copy了,这个没有敲的意义。好,我们在这里把它复制过来,诶大家看,首先就是我们说的这里c.F7报错了对吧?好,先把它住了,这我们知道了,肯定就是他访问了,诶大家看下面又报错了,诶,Get data都报错了啊,Logo没声明对吧,我们去。
43:11
就直接说明出来好,那家看下面这一句报错了。看看它的提示信息。他的member就是成员,Compute not found。或者是not visible就是不可见。那所以这是一个什么意思?对,就是它的内部函数在D里边是不可见的,它是看不到这个内部函数的,那当然大家说如果我用内部调用肯定就不行了,对吧?Get data就是内部调用,所以就是即使我是在一个文件里边,但是它跟它没有继承关系,所以不能采用内部调用的方式去调用它里面的函数。
44:00
那这里肯定就也是不行的,我们把它住掉,哎呀,这个直接就看出来了啊,我们看看这边吧,大家看这个下面又定义了一个EEC啊,也就是说继承了C了,对吧,这是一个继承关系,那大家就能想到是不是在E里边,我们前面不能干的事就都能干了,对吧?对这个internal肯定就可以访问了。大家看啊,下面它是C又用了一个C,这跟这里边的做法是一样的,然后呢,它用c.compute35去算,这个跟这个D里边的做法是一样,这个对吗。对的是吧。有没有问题,大家觉得没问题是吧。大家注意好,我们看一下这边的报错啊,大家已经看到这里是报了错的,这就报错了不行,为什么呢?我们看看它的type a compute not found,明明他继承了C啊。
45:07
因为这个compute这个函数是一个internal类型。所以我们不能用c.C的这种调用方式是这是对,这是外部调用方式,对吧?你不能用外部调用,对直接调就好了,对把C点去掉,直接掉反而就好,所以大家一定要注意,就是我们所说的这个内部调用和外部调用的这个过程,好,这基本上就是,呃,这一点可能是最需要注用的,呃,注意的别的,其实大家就是看看就知道了,对吧?这一点注意一下,就是内部调用的函数,我们不能用C点的那种方式去调用,一定是继承了之后直接调,这才叫内部调用方式。
我来说两句