dxxds 发表于 2013-1-3 14:43:38

【教】给你的故事小游戏,C++兴趣入门(完结)

本帖最后由 dxxds 于 2013-11-23 12:06 编辑

找工作的时候,渐渐发现,很多人对C++非常恐惧,觉得很难啊什么的。
想当年在学校,一个指针,吓跑多少妹子和基佬。
所以,本人想换个方式,给大家入门。

语言嘛,就是拿来叙述事儿的:

我去买了俩鸡蛋。
冬天再大的风也掀不起妹子们那厚重的裙子了……
……

是吧,这就是语言啊。

程序语言呢,就是咱给机器讲故事了,然后某工具翻译给机器,机器看懂后,听你的话。
首先,机器绝对会听你的话,其次,翻译工具很好找,最后,就剩下我教你怎么讲故事了。

不知道现在看帖子的人,有没有自己的翻译工具(编译器)。
有的人,就用自己顺手的(推荐VC6以上,不包含VC6)
没有的人,就去下载一个小编译器吧,入门用的,不用很复杂,DEVCPP,FREE C++这样的就够了。


C FREE 5 比DEVC++ 好用不少,附一個下載鏈接
http://pan.baidu.com/share/link?shareid=336810&uk=1292274826
C FREE 5 在此下載

我在这里用DEVCPP给大家示范
打开编辑器,选文件,选源代码。
这样就开了一个未命名文件。记事本,WORD操作该会的,这里一样,后面就不多说了。
先来一段代码

#include<iostream>
using namespace std;

int main()
{
    cout<<"故事开始"<<endl;
    cout<<"故事结束";
    return 0;
}

这是很基础的一段。大家的英文底子不能太差啊,单词的意思一定要知道才行。
这次先给大家翻译解释,以后不翻译了。

include :包含,就是把要使用的功能所在的文件加到你的代码里。

iostream :IO 是 in/out ,stream流,目前用到的 in就是让你在电脑讲故事时对你的询问有一个回答,回答就给in到电脑里了,out就是让你故事里的事物输出到显示器上。 当然iostream功能不止这点儿,你们先记这么多、

int:integer的缩写,整数,占用电脑四个字节,一可以储存一段数字。一个字节范围0-255,int的最大表示范围就是255*255*255*255.

main:很关键的,主函数,所有故事都是从这里开始讲。

cout :读作 COUT,向显示器输出一段话,<<就是碗里的东西向外倒。
endl :就是 end line 结束一行,然后自行回车换行了。

return:把函数结果交出去。

编写代码的时候,要记着在每一个语句后加分号";",表示句子结束,这也是现今绝大多数语言的规矩。

目前的解释就是这样了,以后所有的代码,都是基于这个往后编的。

可以试着让电脑执行这段代码了,按F9编译运行(C-FREE 用 F5),我们把文件起个名字存起来,就叫stroy_1.cpp吧。

可以看到,命令行窗口出现了,就又立刻消失了。于是大家纷纷表示,我看不到啊。

电脑太听话了啊,干完活就知道立刻退下,好孩子~~

咱们现在让它干完活先留在这,等你吭一声再走。

那就在 return 0 前面加一句   system("pause>nul ")

像这样。

int main()
{
    cout<<"故事开始"<<endl;
    cout<<"故事结束";
    system("pause>nul");
    return 0;
}

pause:很好理解,暂停了的意思,这是一段DOS命令,可以让程序停下来。
system:负责执行这个命令,以后有啥别的命令也可以在这里打

现在这样,我们就看到它把故事讲出来了,哦~ 是样的。

然后,随意按一个键盘按键,就退出了。

好,先将这些,下节讲更多内容。

dxxds 发表于 2013-1-3 15:33:27

给你的故事,C++的兴趣入门 2

本帖最后由 dxxds 于 2013-11-23 12:08 编辑

上节讲了最基本的内容,交了大家怎么让电脑讲故事,这节继续。

我们现在给故事加点内容,让它看起来更充实一点。

今天出去采蘑菇~
采了一筐蘑菇,有50个吧。

好!现在这看起来好多了

完整代码如下

#include<iostream>
using namespace std;

int main()
{
    cout<<"今天出去采蘑菇~ "<<endl;
    cout<<"采了一筐蘑菇,有50个吧。";
    system("pause>nul");
    return 0;
}

我们编译运行,应该会得到你想要的结果,然后我们可以看到,电脑执行了这么一段。

好,我们继续深入。

电脑是给我讲了,但是不能一直给我讲,交谈是有互动性的,我们得互动一下,好!

今天出去采蘑菇~
采了一筐蘑菇,来数数。
有多少个呢?
(然后你来说有几个,输入完数字打回车 )
有XX个呢!

int main()
{
    int mogu=0;                   //蘑菇的数量,就存到这里
    cout<<"今天出去采蘑菇~ "<<endl;
    cout<<"采了一筐蘑菇,来数数。"<<endl;
    cout<<"有多少个呢?"<<endl;
    cin>>mogu;                  //输入一框的数量
    cout<<"有"<<mogu<<"个呢!"<<endl;            //在这里打印数量
    system("pause>nul");
    return 0;
}

现在一开始,我们要告诉电脑,出场的有什么,有个蘑菇的数量,告诉它了,它才会知道有什么,是蘑菇的数量,故事里是多少呢,我还不知道,先给个地方存着吧,然后做好准备。

然后多了一段cin ,读C IN。这就是让我们可以给电脑回信的地方。电脑就会把回信保存起来,就存到>>后面的变量里去。碗向里倒,就是in。

记着,除了被英文标点 引号""括起来的部分,所有标点都必须是英文的。

现在F9编译运行。

会在询问你的地方停下来,然后你乖乖的输入一段数字,程序就会继续执行下去。

好,故事还是太简单了,我们再编的多一点。

今天出去采蘑菇~
采了两筐蘑菇,来数数。
第一框有多少个呢?
(然后你来说有几个,输入完数字打回车)
第二框呢?
(继续输入数字)
两框蘑菇一共有XX个呢!

int main()
{
    int mogu_1=0;                  //第一框蘑菇数量
    int mogu_2=0;                     //第二框蘑菇数量
    int mogu_sum=0;                //两框蘑菇的数量,就存到这里
    cout<<"今天出去采蘑菇~ "<<endl;
    cout<<"采了两筐蘑菇,来数数。"<<endl;
    cout<<"第一框有多少个呢?"<<endl;
    cin>>mogu_1;                  //输入第一框的数量
    cout<<"第二框有多少个呢?"<<endl;
    cin>>mogu_2;                     //输入第二框的数量
    mogu_sum=mogu_1+mogu_2;   //两框的总数量在这里被运算
    cout<<"两框蘑菇一共有"<<mogu_sum<<"个呢!"<<endl;            //在这里打印数量
    system("pause>nul");
    return 0;
}

我们把两框蘑菇的数量分别存到mogu1 mogu2里,然后用mogu_sum把这俩加起来的值保存了。
代码应该没啥难度了。

好,现在来稍微深入一下语言中数字的概念

之前我用到了 int 这种类型来保存,这种变量,只能保存整数,对于小数,就无能为力

故事继续加长

今天出去采蘑菇~
采了两筐蘑菇,来数数。
两框一共有多少个呢?
(然后你来说有几个,输入完数字打回车)
两框蘑菇一共有XX个呢!
平分给家里的三只马里奥,每只能拿XX个

int main()
{
    int mogu_sum=0;                //两框蘑菇的数量,就存到这里
    int mogu_average=0;
    cout<<"今天出去采蘑菇~ "<<endl;
    cout<<"采了两筐蘑菇,来数数。"<<endl;
    cout<<"第两框一共有多少个呢?"<<endl;
    cin>>mogu_sum;
    cout<<"两框蘑菇一共有"<<mogu_sum<<"个呢!"<<endl;
    mogu_average=mogu_sum/3;                        //在这里计算平均每只马里奥能分到几个
    cout<<"平分给家里的三只马里奥,每只能拿"<<mogu_average<<"个"<<endl;
    system("pause>nul");
    return 0;
}

问我们有多少蘑菇,我们试着输入100,因为100不能被平分么 >_<

然后会发现,电脑计算出来的是33,而不是一般数学结果的33.3333……

这里保存平均数的是int类型变量,也就是整数型,它没有小数点什么的,也就只有存整数了

如果我们要试着存小数的话,可以试着这么修改,使用float,就是浮点型,它能保存小数


int main()
{
    float mogu_sum=0;                //两框蘑菇的数量,就存到这里
    float mogu_average=0;
    cout<<"今天出去采蘑菇~ "<<endl;
    cout<<"采了两筐蘑菇,来数数。"<<endl;
    cout<<"第两框一共有多少个呢?"<<endl;
    cin>>mogu_sum;
    cout<<"两框蘑菇一共有"<<mogu_sum<<"个呢!"<<endl;
    mogu_average=mogu_sum/3;                        //在这里计算平均每只马里奥能分到几个
    cout<<"平分给家里的三只马里奥,每只能拿"<<mogu_average<<"个"<<endl;
    system("pause>nul");
    return 0;
}

现在再输入100,它会显示小数了,虽然不够精确,才小数点后四位,不过也算是有小数了,估计小数点后四位的蘑菇也不剩下什么了,是吧?

好,第二讲到此为止。

dxxds 发表于 2013-1-3 16:34:46

给你的故事,C++的兴趣入门 3

本帖最后由 dxxds 于 2013-11-23 12:26 编辑

上一讲介绍了一般数字变量的用法,这次再玩点新的,进度要加快咯

萨!哈吉马路走!

数了一大节课的蘑菇,诸位辛苦了吧?
来,这次换新的,不数蘑菇了,数别的……

家里的三只马里奥说上节课的蘑菇吃多了。
看来是想换花样,要换吗?
(选择换,还是不换)
那就换吧 / 我还是想给他们吃蘑菇~~

上代码

int main()
{
    int food_id=0;                      //选择题的编号,选好存起来
    cout<<"家里的三只马里奥说上节课的蘑菇吃多了。"<<endl;
    cout<<"看来是想换花样,要换吗??"<<endl;
    cout<<"(按 1)换(按其它键)不换"<<endl;
    cin>>food_id;                     //选你想给马里奥们的食物

    if(food_id==1)                  //判断 你选的是不是1
    {
       cout<<"那就换个别的吧"<<endl;
    }
    else
    {
         cout<<"蘑菇明明很好吃啊,不换!"<<endl;
    }
    system("pause>nul");
    return 0;
}

这故事讲的浅显易懂,注释里都写了

双等于号 == 就是判断俩值是否相等的意思,等于就是真,不等于就是假
( 1==1)真
(100 == 1) 很明显是假的

一个等于号 = 是传值,只是传值,把右面的值传给左面的变量
a=100; a变成了100
a=200;a又变成了200

现在来讲一下if语句

这是一个在各种编程语言里都能见到的判断语句

if(条件)
{
   条件为真的话 就要干啥
}
else
{
   否则的话 就干啥
}

这个判断语句还可以写的更长,因为时常会出现多个情况,你得选一个

家里的三只马里奥说上节课的蘑菇吃多了。
看来是想换花样,上山采点什么呢?
1蘑菇 2梨子 3苹果
(然后你说采什么)
那就让他们吃XX吧!

int main()
{
    int food_id=0;                      //选择题的编号,选好存起来
    cout<<"家里的三只马里奥说上节课的蘑菇吃多了。"<<endl;
    cout<<"看来是想换花样,这节课吃什么呢?"<<endl;
    cout<<"1苹果 2草莓3苹果 "<<endl;
    cin>>food_id;                     //选你想给马里奥们的食物

    if(food_id==1)                  //判断 你选的是不是1
    {
       cout<<"好!就吃苹果"<<endl;
    }
    else if(food_id==2)                   //判断 你选的是不是2
    {
         cout<<"好!就吃草莓"<<endl;
    }
    else if(food_id==3)                   //判断 你选的是不是3
    {
         cout<<"好!就吃苹果"<<endl;
    }
    else                   //判断 当你选的既不是1也不是2又不是3的时候
    {
      cout<<"别乱按啊,按错了吧?"<<endl;         
      cout<<"没吃的了!哼!回去了!"<<endl;          //我们这位上山采食物的妹纸因为你胡说八道生气了
    }
    system("pause>nul");
    return 0;
}

这样一来,语句就变长了 用 else if 来加长这个条件判断就行了

if(条件1)
{
   条件1为真的话 就要干啥
}
else if(条件2)
{
条件2为真的话 就要干啥
}
else if(条件3)
{
条件3 为真的话 就要干啥
}
else
{
   以上条件都不满足的话 就干啥
}

故事好像有点意思了

最后,我们再玩个另类的,故事依然不变,但是用另一种功能实现

int main()
{
    int food_id=0;                      //选择题的编号,选好存起来
    cout<<"家里的三只马里奥说上节课的蘑菇吃多了。"<<endl;
    cout<<"看来是想换花样,这节课吃什么呢?"<<endl;
    cout<<"1苹果2草莓3蘑菇 "<<endl;
    cin>>food_id;                     //选你想给马里奥们的食物

   switch(food_id)
    {
       case 1:                                 //当 food_id 是1的时候
            cout<<"好!就吃苹果"<<endl;
            break;
       case 2:                                 //当 food_id 是2的时候
            cout<<"好!就吃草莓"<<endl;
            break;
       case 3:                                 //当 food_id 是3的时候
            cout<<"没别的了,接着吃蘑菇去"<<endl;
            break;
       default:                                 //当 food_id 不是1,2,3的时候,做默认处理
            cout<<"别乱按啊,按错了吧?"<<endl;         
            cout<<"没吃的了!哼!回去了!"<<endl;         //我们这位上山采食物的妹纸因为你胡说八道生气了
            break;
    }
    system("pause>nul");
    return 0;
}

这是一个switch case结构 一样可以完成判断 利用条件不同 选择不同的分支
代码按顺序执行,一个一个判断
执行完后,break 跳出 switch 语句。故事继续进行
如果没有 break 代码会继续向下走,执行下一个分支的内容
比如

int a=1;
switch(a)
{
case 1:
    cout<<"吃河蟹"<<endl;      //然后没有break
case 2:                                       
    cout<<"吃虾米"<<endl;      //这里的2也被执行了
case 3:                                       
    cout<<"吃虾米"<<endl;         //这里的3也被执行了
    break;                                     //到这才跳出
case 4:
    cout<<"绝食!"<<endl;       //这条BE没被执行,可喜可贺
    break;
}

C#中,没有break会报错。

代码总的来说简单明了,注释都写清楚了 不懂的可以问我

continue这句能用上的时候 非常非常少,所以先不解释了,几乎所有逻辑都能用上面语句执行,continue就不讲了,有兴趣的自己找书看

第三讲到此为止


dxxds 发表于 2013-1-12 18:36:14

给你的故事,C++的兴趣入门 4

本帖最后由 dxxds 于 2013-11-23 12:24 编辑

    自从前几节课,马里奥吃了过多的蘑菇,就不停的长大,后来身高爆棚,房子塌了。他们的事情就告一段落。
   
      话说。有一位公主,被邪恶的吐火龙关在了一个巨大的城堡里,龙除了一日三餐不间断的喂公主之外,就啥都不干,静静的等待正义的王子来解救公主。
      每隔几日,公主就要去视图刺杀这只邪恶的龙,不过总是会以各种原因失败。
      今日,又刺杀失败……,被抓住
      公主:放开我!
      火龙:……
      公主:你既不杀我,又不XX我,那为什么还不放了我!?
      火龙:……

于是,我们来看看今天的刺杀过程

int main()
{
    int gong_zhu_HP=200 , gong_zhu_AT=10; //公主的血量公主的攻击力
    int dragon_HP=1000 , dragon_AT=100;//龙的血量 龙的攻击力
   
    while( gong_zhu_HP > 0 || dragon_HP > 0 )
    // 当公主 或者 龙的 血量大于0时 我们就一直循环下面括号里的逻辑
    {
      cout<<"公主的血量:"<< gong_zhu_HP<<"               ";
      cout<<"火龙的血量:"<< dragon_HP<<endl<<endl<<endl;
      system("pause>nul"); //暂停,按任意键继续
      cout<< "公主对龙发动偷袭,造成"<< gong_zhu_AT << "点伤害"<<endl<<endl<<endl;
      //这里公式很简单 有多少攻击力 就能对对方造成多少伤害 防御力神马的日后再说
      dragon_HP -= gong_zhu_AT; //这是简写 a-=b 相当于 a=a-b 先运算 a-b 再赋值给 a
      system("pause>nul");//暂停
      if(dragon_HP <= 0) //每攻击一次 就判断一下 是不是有人挂了 挂了就跳出这段循环
      {
          cout<<"火龙挂了"<<endl;//跳出前先给个游戏提示 GAME OVER 这样的
          break; //跳出
      }
         
      cout<<"公主的血量:"<< gong_zhu_HP<<"               "; //显示一击后的数据
      cout<<"火龙的血量:"<< dragon_HP<<endl<<endl<<endl;
      system("pause>nul"); //暂停
      cout<< "火龙对公主发动反击,造成"<< dragon_AT << "点伤害"<<endl<<endl<<endl;
      gong_zhu_HP -= dragon_AT;
      system("pause>nul"); //暂停
      if(gong_zhu_HP <= 0)
      {
          cout<<"公主被抓"<<endl;
          break;
      }
    }
    system("pause>nul");
    return 0;
}
运行一下 按任意键可以继续观看

多么的触目惊心啊……
如果你是完全按照我写的代码抄来运行的话,那结局应该是公主挣扎了一次后被抓,BE ……

这里出现的新概念
|| :或者 或当两者一种一个为真的时候 就是真
&&:并且 与当两者都为真 才是真
这是过去数学里真值表的概念 我就不多讲了

while 循环

while( 条件 )
{
代码
}

当条件为真的时候 就执行下面的代码
执行完一遍后 继续判断条件
当条件依然是真的时候 就再执行一遍
当条件是假的时候 就执行while的括号后面的代码

情况就是这样

这是第一种常见的循环代码

下面是第二种循环

我们英俊潇洒风流倜傥玉树临风威武不凡人见人爱龙见自裁的天下第一王子大人出现了。
为了我们伟大的泡妞事业…… 伟大的拯救事业,我们要层层深入敌穴,勇夺公主!

int main()
{
    cout<< "我们英俊潇洒风流倜傥玉树临风威武不凡人见人爱龙见自裁的天下第一王子殿下出现了。"<<endl<<endl<<endl;
    for ( int i = 1 ; i < 11 ; ++i )
    {
      system("pause>nul");//暂停
      cout<<"王子在路上砍死了"<< i <<"只火龙手下的小怪物"<<endl; //基本输出语句 自己阅读
    }
    cout<<endl<<endl<<endl;
    cout<<"王子殿下砍死了所有小怪,来到了公主面前"<<endl;
    system("pause>nul");
    return 0;
}

这段代码,是讲述了,我们的王子是如何一路过关斩将,砍死了无数小怪之后,才见到BOSS的。可歌可泣的血泪史啊

这个for 循环里有三个参数 中间用分号隔开

(int i =1 ; i<11 ++i )
第一句 在for里声明了一个变量 i : 这个 i 用来数我们的王子殿下杀了多少小怪
第二句 是循环条件,为真就执行括号里的代码,为假就结束: i<11 是说,要杀小于多少个的小怪才结束
第三句 是执行完括号里的代码后 我们要干啥 :++i 每次i 自加1 , ++i 相当于 i+=1i=i+1 只是效率更高一些

执行顺序是

创建int i
判断 i 是否小于 11 结果是真
王子砍怪兽
被杀死怪物数量加1
循环
……
直到i 等于 11的时候 才跳出

现在我们的王子已经来到了公主面前!王子与龙的大战即将开始!那王子到底最后能不能成功拯救公主呢? 我们下节再讲。
看完的要回帖!





dxxds 发表于 2013-1-31 15:44:18

本帖最后由 dxxds 于 2013-1-31 15:47 编辑

话说,上次1月13号更新完,14号就被领导整着出差去了,出差地方没网络。
今天(31号)才刚回,晚上也要坐火车回家了。
既便是赶着过年了,那后面的也会继续更新,只是会比较短了。
虽然每一个男人都很讨厌被别人说短,不论是长度还是时间的。
但是短小精干这个词的意义就在于,只要是个男人,不管是长是短,能让你们到达GC的。那就是真爷们儿!
好了,准备坐车了。回去了会速速更新,先闪了。

dxxds 发表于 2013-2-20 22:48:58

给你的故事,C++的兴趣入门 5

本帖最后由 dxxds 于 2014-10-5 19:23 编辑

看到大家这么期待,我十分十分的开心。
很对不住,这么久没更。主要是回家后,就没有机会碰电脑了。
父母俩人轮着上,离家一年,父,母和这台电脑已经磨合的相当好了,电脑的行程已经被排的满满的,容不得我插一腿了……

现在接着来

这次只讲一个内容,函数


话说上次王子在城堡里见到了公主,打倒了魔龙,真是可喜可贺
于是,公主成为了王子队友(为什么不是老婆啊?因为现在这个游戏还没有结婚系统……)

剧情接下来,就到了必须让人耳目一新的桥段了啊
城堡现在可以慢慢的坍塌了…… (汗……)

于是王子抓住了公主的手,说:跑吧,这儿要塌了!

随后就看到两个身影拼命的向大门口跑
就在快到门口的时候,我们耳目一新的剧情出现了:钟楼掉了下来,砸断了原本架在护城河上的木桥
于是乎
公主反拽住了王子的手,说:跟我来,我们顺着地下密道走!
只是,这密道里的陷阱暗器什么的也不少

我们来试着写一个陷阱函数

#include<iostream>
using namespace std;

void stage_1(int one,int two) //陷阱函数
{
      int result = one + two; //计算结果
      if(result == 100)
      {
                cout<<"躲过陷阱"<<endl;
                cout<<"俩人逃离城堡"<<endl;
      }
      else
      {
                cout<<"被陷阱击中"<<endl;
                cout<<"俩人都死了,游戏结束"<<endl;
      }
}

int main()
{
      cout<<"进入密道,第一个陷阱"<<endl;
      cout<<"请输入两个和为100的数"<<endl;
      cout<<"成功将躲避陷阱,失败将被击中"<<endl;

      int a = 0 , b = 0; //要输入的两个数 分别记录到a和b里
      cin>>a>>b; //输入 a b 两个数

      stage_1(a,b); //将a b传入函数,执行上面的函数

      system("pause>nul");
      return 0;
}

稍微认真的读一下代码 很简洁的

函数是把一段代码封装了的功能,很好用
当一段代码需要被反复使用的时候,可以试图将其封装起来,然后再在需要调用它的地方,写上一行这个函数名,就行了,比每次调用都写一大长串代码要方便简洁易懂的多

比如 我想把这个陷阱调用5次

顶多会写成
stage_1(a,b);
stage_1(a,b);
stage_1(a,b);
stage_1(a,b);
stage_1(a,b);

简洁点就是
for(int i=0;i<5;++i)
stage_1(a,b);

总比那一长串代码来五次要好看的多
而且要修改这个陷阱的时候,只需要改一次函数,以后所有代码里的陷阱就都跟着变了
所以十分推荐将一部分功能写成函数

下面再来点简单的函数例子
void add(int left , int right)
{
      cout<<left + right;
}
int main()
{
      add(55,60); //调用了这个加法函数 输出115
      return 0;
}
执行加法 并且输出计算结果

void 表示函数运行完后不反回任何结果

(int left , int right) 表示函数可以接收的参数,或者是变量,或者是指针,或者是对象什么的,自己看着写

再看下一个变种

int add(int left , int right)
{
      int result = left + right
      return result;
}
int sub(int left , int right)
{
      return left - right;
}
int main()
{
      int a = add(55,60);
      cout<<a<<endl;输出115

      cout<<sub(99,89); //这是减法函数输出10
      return 0;
}

看到return了吧,这是一个返回值,表示把函数自身执行完的结果返给调用它的那一层
不能理解的话,可以简单的把带返回值的函数,理解成变量,这个变量的值就是被返回的值

函数可以返回任意类型的值,上面写的是int
下面还有别的可以写

char* who_is_the_most_handsome_man()
{
      return "dxxds! dxxds!I LOVE YOU !!!";
}
int main()
{
      cout<<who_is_the_most_handsome_man()<<endl;;
      return 0;
}

行了,就这么多内容,以后还有关于函数的其它注意事项,那就等下次再说了。



dxxds 发表于 2013-2-21 15:59:04

有人反映,說DEV寫代碼不方便,這次換個方便點的

http://pan.baidu.com/share/link?shareid=336810&uk=1292274826

C-FREE5 我上傳好了

dxxds 发表于 2013-2-21 21:47:02

给你的故事,C++的兴趣入门 6

本帖最后由 dxxds 于 2013-11-23 12:20 编辑

这次开始讲指针
为了能让大家迅速理解这个东西
我决定用十分生动形象的方式来讲解

那就是游戏

好了,废话不多说,开始了

指针,就是一段内存地址

如果电脑是32位的,指针就是4个字节,因为一个字节是8位,所以 32/8 = 4。
如果是64位的,它就是8个字节,64/8 = 8。

好,开始上游戏

首先用play station 1 上的一款战棋大作《梦幻模拟战2》来演示什么是指针的地址。

上图

D:\11.png

目前看到的是主人公(艾尔温)的属性,攻击力26,防御力18,魔法量9/9,移动力5。

d:\12.png

这是用修改器搜索到的艾尔温的数据,用的十六进制表示。
分别是1A 12 05 09 09
转换成对应十进制的数字就是 26 18 05 09 09

左上方的 00000000009FF99B 这一大长串数字,就是目前攻击力26在内存中的地址,用的十六进制表示
因为我的系统是64位的,所以这个地址比较长,占了8位

现在复习一下一个概念,就是一个字节有多长
简单的来说,一个字节用十六进制表示,就是从00-FF,十进制就是0-255
现在看一下这个攻击力的地址 00000000009FF99B 就会发现它是由16个十六进制数组成
每2位十六进制数组成一个字节,16个数字就是8个字节。

如果是32位系统,那么这个指针地址应该会比我这个短一半,比如像这样 003CA953,
32位机子的指针,变化范围是从00000000 - FFFFFFFF ,也就是从 0 - 4G
所以32位系统的内存容量大于4G也没用,因为指针指不到那个位置。
64位32位机子的指针,变化范围是从0000000000000000- FFFFFFFFFFFFFFFF ,也就是从 0 - 4G*4G
这个4G*4G,可是一个相当大的数字了,具体是多少么,乃们有兴趣就自己去算吧,我懒得算,反正很大。

于是乎,想体验DDR3白菜价内存的童鞋们,还是整64位系统吧,然后来它个24G内存,玩它个内存3P,不对,是三通道,是吧?

好了,接下来看防御力

d:\13.png

防御力(就是图中的十六进制数:12)的地址在 00000000009FF99C

发现它只比攻击力(就是图中的十六进制数:1A。地址在 00000000009FF99B)的地址多了1

看看,内存里的数据也是一样的,每2位十六进制数组成一个字节

也就是说,攻击力这个数据,只占了一个字节,那么它的最大值最高只能到FF,也就是255

也就是说,这个数据是一个char类型数据(准确的说应该是unsigend char),char只占了一个字节

再回忆一下前面讲的,char 占一个字节,short占两个字节,int 和 long 占4个字节

现在来试着一下艾尔温的属性数据的C++代码吧

char at = 26;   //攻击力
char df = 18;//防御力
char move = 5;//移动力
char mp_now = 9;//目前的魔法值,范围0-9
char mp_up = 9; //魔法值上限,不升级的话,固定不变

为什么用char,也就是一个字节储存数据呢,因为这是老游戏,当年的机子内存小,消耗不起。

老游戏说完了,现在说个新一点的

来看看另外一款游戏,《三国无双6 猛将传》


可以看到,这位是我的蔡文姬,超萌的。
就是目前很穷,金钱才79(被我修改小的)
这是搜索到的数据和地址


金钱的数据:00 00 00 4f,也就是十进制的 79。
金钱的地址: 0000000000B58698

内存中,左面(低地址位)是低位,右面(高地址位)是高位,和书写正好相反。这个书写什么的,了解,会看就行。

可以看到,好像有四位,我现在改大一点


把高位填了一些数字
改成了 00 08 ff 4f

第四个字节还是00,我没改,因为太大了,改完后会完全超出游戏支持的金钱显示位数上限,也就是看不出来。

看看游戏里是多少了



嗯~ 58W了,很好很好

那可以看出,这个数据最高有四个字节,(为什么不是三个?因为没有三个字节的数据类型),也因为只有四个字节,所以第五位 40 没有被计算在内,否则就不是79,而是超大的数据了

现在可以写一下这个金钱的代码了

int gold= 79;

好了,地址什么的讲完了,现在再讲指针就轻松了

假设我们想用一个指针来保存金钱的地址(地址也只能用指针保存),那么可以这么写

int gold= 79;//刚才的金钱
int* p = NULL; //定义了一个指针,将未使用的指针赋位空(指针安全了)
p = &gold //用 & 取得金钱变量gold的地址 保存到了int* 型变量里

或者这样写
int* p = &gold //可以将前两行写成一行

指针类型都是 在某种类型的后面加* 确定的,而且不论是什么数据的指针,它的大小都是一样的,要么就是四个字节(32位程序),要么就是八个字节(64位程序)。

我推荐把星号* 写到前面,写成像 int* long* 这样,因为方便理解,表明它是一种指向某一数据类型的指针

这时 p 的值就是 0000000000B58698
然后我们用*号取这个p里的地址
*p 的值就是79

指针入门课程结束

小小的总结一下指针部分

首先
int gold = 79;
int* p = &gold;
于是
gold是一个值,值是 79
*p 是一个值,也就是gold,值是 79,”*“可以取任何地址里的值,也就是取值符,解引符。

&gold是一个地址,值是 0000000000B58698 ,”&“可以取任何值的地址,也就是取址符(内存地址,不同人的机子,这个地址会不同)
p是一个指向地址的指针,也就是gold的地址(&gold),值是 0000000000B58698(内存地址,地址会有变化)


好,本节结束

看完不回帖的,木有小JJ



dxxds 发表于 2013-2-23 00:56:27

给你的故事,C++的兴趣入门 7

本帖最后由 dxxds 于 2013-11-23 12:18 编辑

话说,马里奥家被蘑菇撑爆了
后来咋样了?

是碧琪公主挽回了局面
把马里奥的蘑菇换成了毒蘑菇

于是那根壮硕V5的马里奥萎了回来……

上代码

#include<iostream>
#include<string>
using namespace std;

void change_mogu(string mogu_1 , string mogu_2) //一个负责把两个蘑菇互换的函数
{
      string tem = mogu_1; //一个临时变量
      mogu_1 = mogu_2;      //交换蘑菇
      mogu_2 = tem;             //交换完成
      cout<<"碧琪公主交换了蘑菇"<<endl<<endl;
}

int main()
{
      string mogu = "蘑菇";
      string dumogu = "毒蘑菇";
      cout<<"马里奥正在吃"<<mogu<<endl<<endl;
      change_mogu(mogu,dumogu); //准备互换mogu和dumogu
      cout<<"马里奥正在吃"<<mogu<<endl<<endl;
      return 0;
}

我在上面写了一个change_mogu的函数,算法很显而易见,就是要把俩蘑菇的值互换一下

运行输出

咦? 怎么没换? mogu 怎么还是蘑菇? 怎么不是毒蘑菇啊????
蘑菇你怎么了? 碧琪公主你这是要闹哪样啊?
这个函数的算法有问题么?

啊~ 哈哈 我们来分析一下这个函数干了什么

首先,这个函数通过参数表创建了两个变量
string mogu_1;
string mogu_2;

然后在主函数中被调用
参数传入
mogu_1 = mogu; //蘑菇
mogu_2 = dumogu; //毒蘑菇

然后交换,交换完成后
mogu_1 //毒蘑菇
mogu_2 //蘑菇

但是,外部变量没有变化
mogu; //蘑菇
dumogu; //毒蘑菇

接着,mogu_1和mogu_2因为函数的执行结束,随着函数一起销毁了
最后输出了"马里奥吃的是"mogu。
mogu没有发生变化

额…… 这怎么整?
显然这么干是不行的,有办法么?
有的,上节课不是讲了指针么,指针可以记录变量的地址
那就用指针记录外部变量,mogu 和 dumogu 的地址就行了
直接操作 mogu 和 dumogu

来吧,我们改代码

void change_mogu(string* mogu_1 , string* mogu_2) //把string类型换成了string*类型
{
      string tem = *mogu_1; //别忘了用 * 来取地址里的值
      *mogu_1 = *mogu_2;      //交换蘑菇
      *mogu_2 = tem;         //交换完成
      cout<<"碧琪公主交换了蘑菇"<<endl<<endl;
}

int main()
{
      string mogu = "蘑菇";
      string dumogu = "毒蘑菇";
      cout<<"马里奥正在吃"<<mogu<<endl<<endl;
      change_mogu(&mogu,&dumogu); //把mogu和dumogu的地址传了进去 记着用 & 取变量的地址
      cout<<"马里奥正在吃"<<mogu<<endl<<endl;
      return 0;
}

再执行一次
可以看到碧琪公主已经拯救了马里奥,他可以萎了

这便是取地址的妙用
说到取地址,就不得不说一下C++的相对C新增加的一个特性
那就是,变量的引用

干什么的呢?
就是给变量再起一个名字,新旧名字都是在叫它

像 Super Mario Bros 我们就同时用 超级玛丽 和 采蘑菇来叫这个游戏
不论是说 超级玛丽 还是说 采蘑菇
都是在说它

好,意思明白了,上代码

string happiness = "幸福";
string& xingfu = happiness;
cout<<"请问,你幸福吗?"<<endl;
cout<<xingfu;    // xingfu:幸福

于是,下面这段脍炙人口的……

string happiness = "幸福";
string& xingfu = happiness;
xingfu = "我姓福"            //在这里修改一下 xingfu
cout<<"请问,你幸福吗?"<<endl;
cout<<happiness;    // happiness:我姓福

就是可以这么表达了
也就是说 xingfu 和 happiness 等价了

于是乎,上面的例子可以用引用来表达
就是

void change_mogu(string& mogu_1 , string& mogu_2) //只在这里换成引用,后面全都和最初始的代码一样
{
      string tem = mogu_1;
      mogu_1 = mogu_2;      //交换蘑菇
      mogu_2 = tem;               //交换完成
      cout<<"碧琪公主交换了蘑菇"<<endl<<endl;
}

int main()
{
      string mogu = "蘑菇";
      string dumogu = "毒蘑菇";
      cout<<"马里奥正在吃"<<mogu<<endl<<endl;
      change_mogu(mogu,dumogu); //这里对外部变量起了新名字:string& mogu_1 = mogu; string& mogu_2 = dumogu;
      cout<<"马里奥正在吃"<<mogu<<endl<<endl;
      return 0;
}

运行程序,你会发现碧琪公主又一次成功的交换了蘑菇

OK,大功告成,本讲结束

俺的诅咒才没有那么狠毒 : 看完不回复的都没有小JJ

dxxds 发表于 2013-3-11 10:48:35

给你的故事,C++的兴趣入门 8

本帖最后由 dxxds 于 2013-11-23 12:17 编辑

指针结束,那就可以讲数组了

不是我忘了讲,只是我觉得,数组和指针一起讲的话,可能会更好理解

群雄并起,最终曹操干掉了袁氏家族,周瑜一把火烧了曹操的八十万大军,诸葛亮从周瑜手里偷到了荆州
从而三分天下 ……   

这时,碧琪公主觉得自己手下的蘑菇人太多了,除了马里奥,谁也记不住,于是决定给他们编号:"蘑菇人1 蘑菇人2 ……"
让他们自己报数

上代码

#include<iostream>
#include<string>
using namespace std;

int main()
{
      string mogu_man; //string(字符串)类型的数组 里面有20个string
      char what_mogu_say ; //char 类型数组,里面最多储存20个字母
      for(int i = 0;i < 20; ++i)
      {
                sprintf(what_mogu_say,"蘑菇人%d",i+1); //生成每一个蘑菇要说什么话
                mogu_man = what_mogu_say; //告诉每一个蘑菇人,在报数时要说什么
      }

      cout<<"碧琪公主:“现在,开始报数!”"<<endl; //公主开始让蘑菇人报数了
      for(int i = 0;i < 20; ++i)
      {
                cout<<mogu_man<<endl; //自己看输出结果
      }
      return 0;
}

已经可以看到20个蘑菇人报数了

string mogu_man 一开始就申请了20个string,比自己写20个string变量要方便,而且这个数量可以自己改,50 100 5000 ……,不用数组会写死你

还有这个 char what_mogu_say
这是一个变量,因为char 类型只能保存一个字母,所以有时候我们需要写一段话的时候,会不那么方便,所以有这么一个数组类型,io函数支持这个数组char[]整句输出,会方便很

多。并且因为是数组,所以支持逐字编辑,每一个字都能单独编辑修改什么的

sprintf 这个也稍微说说,它和printf用法一样,只是printf是把后面的字符串输出到屏幕上,sprintf是把后面的字符串输出到第一个char数组里面,就是这么回事。

关于数组,重要的是比之前申请的变量都多了一个 ,X就是里面每一个变量的编号,叫数组下标。从0开始,不是1,也就是说,20个蘑菇人编号是 0 - 19 ,而不是1 - 20,所以我

在这里i加了1;
sprintf(what_mogu_say,"蘑菇人%d",i+1)

然后讲讲数组给函数传参的情况,之前说过,函数传参的过程,是一个创建新值然后再赋值的过程,我再写一次
有个加法函数 add(int left,int right)
然后我传进去个俩要相加的数据
int a=10,b=20;
add(a,b);

这传参期间执行了一次创建并赋值
就是创建了left,right
int left=a,right=b;
然后在函数执行完毕后把left right删了

那现在我想传一个数组的时候,情况是怎么样的
假设有个数组,又有个函数,函数负责把数组里的数字都加1

int a={1,2,3};

void add_1(int a[]);
上面这么写的是对的,只写一个数组就行了,数组下标不用写,当然写了也行,只不过都是白写
void add_1(int a)
这样写和上面写的是一样的,它不会因为你多写了个2,就会把数组里第三个元素扔进去的
我写一下内部实现

void add_1(int a[])
{
      for(int i=0;i<3;++i)
      {
                ++a;
      }
}

看到了么,这里还会重新操作数组下表,填什么都无用,所以还不如不填
为什么?
原因是,这个数组,其实是一种指针,当a的时候,实际上是一个指向数组第一个元素的指针,然后a表示,在第一个元素地址的基础上,偏移了一个int单位地址跨度。
那我们写void add_1(int a)的时候,是不是把数组里第三个元素的地址穿进去了呢?
答案是没有,依然传进去的是第一个元素的地址。

还可以这么写
void add_1(int* a) 表示一个int型指针,指向了数组的第一个元素,函数内容依然不变。

给你们看一个新手玩数组容易犯错的例子
int a={2,3,4};
然后我想输出它们,于是写成
for(int i=1;i<3;++i)
cout<<a<<endl;

这个起始下标的i=1就错了,因为数组下标的第一个数字是0.不是1,所以要改成
for(int i=0;i<3;++i)
cout<<a<<endl;

这样就行了。

好,数组就讲这么多,剩下更多的数组内容,自己日后使用到的时候再学习吧,原理都是一样的




dxxds 发表于 2013-3-14 13:46:15

给你的故事,C++的兴趣入门 9

本帖最后由 dxxds 于 2013-11-23 12:16 编辑

现在讲一下C++的类

这是好东西啊,什么叫面向对象? 就是你的代码主要是表述对象的行为
为什么要面向对象?因为你不面向对象的话,你就不能看到她细腻的肌肤,娇羞的动作和让你火辣的行为……

激动了么? 那现在就开始讲

class 类

从字面意思来讲就是指只某一类东西
从生物学来讲就是有共通行为和来源的一系
从物理学来讲就是有相同属性的一类东西
从……

基本上就是这样的意思了

现在先写一个超~~~级~~~简单的类,让大家了解一下

class 玛丽奥
{
    string 名字;
    int    体重;
    float身高;
};

每一样东西都有自己的属性,我们就可以把它们归类在一起,写到一个类里。
具体这样做的原因就是方便数据的管理,还有日后写代码的数量也会少很多

玛丽奥就是这个类的名字
下面三个变量就是这只玛丽奥的属性,当然玛丽奥的属性不止这点儿,还有颜色,职业,骑龙否…… 不过我就先不写这么多了。

现在上真代码

class Mario
{
public:
    string name;
    int    weight;
    floatheight;
};

好!写完了,看起来是不是很简单
只是多了一个 public ,意思是说这下面的数据都是对外公开的,可以随意调用,被外部函数调用,也可以被内部函数调用。

不懂没关系,一会儿就懂了

那怎么用呢,咱就这么用,接下来写咱们熟悉的main函数

int main()
{
    Mario p1;          //玩家1 player1,目前就一个人玩,也就是我,或者你
    p1.name="mario";      //名字,玛丽奥,红裤子的
    p1.height=1.2;   //身高一米二 ,吃了蘑菇就会被拽到二米
    p1.weight=30;      //30公斤,吃了蘑菇会被撑到90公斤,我不会告诉你蘑菇的重量=大玛丽奥的weight-小玛丽奥的weight=60公斤的
      
    cout<<"玛丽奥的名字:"<<p1.name<<endl;
    cout<<"玛丽奥的身高:"<<p1.height<<endl;
    cout<<"玛丽奥的体重:"<<p1.weight<<endl;
    system("pause");
    return 0;
}

我们看到,现在除了一些常见的变量类型(int float char ……)之外,我们还看到了新的类型:Mario类型,说明这个玛丽奥类生效了,我们创建了一个新类型。并且定义了一个Mario类型的变量p1,最后还用了“.”运算符,就是英文句号访问到了类内部的变量

这就是最基本的类调用了。

那么,这节课结束了……



才怪!

显然我还没有给你们讲public是在回事。

弄完变量之后,就该弄函数了。

函数我们也可以写到class里面去,然后我们就可以讲解这个public的作用了。

就写一个能显示玛丽奥状态的函数吧

class Mario
{
public:
    string name;
    int    weight;
    floatheight;
   
    void show_state()
    {
      cout<<"玛丽奥的名字:"<<name<<endl;
      cout<<"玛丽奥的身高:"<<height<<endl;
      cout<<"玛丽奥的体重:"<<weight<<endl;
    }
};

void show_state()就是这个函数了,都看懂了吧
我把之前的部分抄上去了,然后把P1删了,因为是自己人,变量是啥都知道,不用传参了就

然后是调用

int main()
{
    Mario p1;          //玩家1 player1
    p1.name="mario";      //名字
    p1.height=1.2;   //身高一米二
    p1.weight=30;      //30公斤
      
    p1.show_state();      //和之前的一样 显示数据

    system("pause");
    return 0;
}

现在来讲一下public,这玩意是为了区分类中的成员变量和成员函数是否可以被外部使用的标记
如果不写,空着,那默认就是private,也就是私有,私有就是说,private下面的部分不能被外部调用
什么是被外部调用? 就是不是被类函数调用的,就统统是外部
之前一开始写的调用就是外部调用,被外部的main函数调用了。

那现在我们再写一个私有的,对比一下就能明白一些了

class Mario
{
private:
    string name;
    floatheight;
    int    weight;

public:                   //我把public改到这里了 上面部分的数据我弄成私有了
    void init_data()
    {
      name="mario";      //名字
      height=1.2;   //身高一米二
      weight=30;      //30公斤
    }
    void show_state()
    {
      cout<<"玛丽奥的名字:"<<name<<endl;
      cout<<"玛丽奥的身高:"<<height<<endl;
      cout<<"玛丽奥的体重:"<<weight<<endl;
    }
};      // ← 好多时候,都会忘了这里的分号,丢了的话,要写上

int main()
{
    Mario p1;          //玩家1 player1
    p1.init_data();   //外部不能调用数据了 所以改成函数调用
    p1.show_state();
    system("pause");
    return 0;
}

现在,玛丽奥类里面,就只有两个成员是公开的了,init_data 和 show_state

别的都不能访问,如果你现在还在main里写p1的初始化数据的话,就会出错。

那么这节课就讲到这里,后续内容,后续再写

dxxds 发表于 2013-3-17 10:29:20

给你的故事,C++的兴趣入门 10

本帖最后由 dxxds 于 2013-11-23 12:26 编辑

都已经学到类了,那咱们就可以做一些有意思的东西,和做一些很大的程序了,哦哈哈哈~~

所以在这里稍微的补充一点常识,就是关于内存分配的部分

一般我们在申请变量的时候,都要把变脸分配到一个内存空间里去

这个空间目前告诉你们其中的两个区(当然不止两个),分别是堆区和栈区

而我们目前的数据,全都分配到栈区了

这个区有一些好处:速度快,资源自动释放

然后这个区的缺点:空间很小,只有1M(具体看编译器设置了,但是都不会很大)

所以如果我们一直这么写程序的话,会出现数据申请失败的情况

接下来我说一下堆区,这里的数据是要自己申请和自己释放的,速度比栈区慢,但是空间很大,4G(32位机子)

像类这种数据量比较大的类型,我们一般会把它申请到堆中去

那现在就来给你们示范怎么申请堆数据

int main()
{
      int a = 10;               //栈空间
      int* b = new int(20);   //堆空间

      cout<<"a:"<<a<<endl;
      cout<<"b:"<<*b<<endl;

      delete b;                //b不会被系统自动回收,所以我们得自己写一下delete
      system("pause>nul");
      return 0;
}

这里用了一个新的关键字 new,new 会返回一个指针地址

表示在堆中申请一段空间,这个空间的大小是 int(四个字节)

然后把这个空间的首地址给了 指针 b

然后我们输出了 指针b里的数据

最后用delete关键字 删除b空间里的内容

OVER

听懂了吧,那么以此类推,这次我们把玛丽奥的数据申请到堆里

class Mario
{
public:
      string name;
      void init_name(const char* n)
      {
                name = n;
      }
      void show_name()
      {
                cout<<name<<endl;
      }
};

int main()
{
      Mario player_1;
      player_1.init_name("玛丽奥");
   
      Mario* player_2 = new Mario;
      (*player_2).init_name("路易基");
      
         cout<<"玩家1:";
      player_1.show_name();
      
         cout<<"玩家2:";
         (*player_2).show_name();
         
         system("pause>nul");
      return 0;
}

这样就可以看到,这两种申请内存的方式都成功了,玩家1是对象,玩家2是指针对象

这里出现了一个新的关键字 const
这里表示,它后面修饰的部分是一个常量,就是创建后就不能被修改了的意思

示范一下
const string gender_of_mario = "男"; //创建了一个常量:玛丽奥的性别(性别这玩意一般来说不能修改了……)

gender_of_mario = "女";//会在这里妥妥的出错,不会编译通过

关于const就讲这么多,下面继续。

不过看调用玩家2的样子,总觉得有点别扭啊:不但要用*解引用,还要再在外面把它用括号括起来。写起来十分蛋疼

所以在这里,C语言给提供了一个不错新符号,用来代替这个

就是用 "->"

给你们示范一下

(*p).name;

p->name;

这俩等价,那我把上面的代码重写一次

int main()
{
      Mario player_1;
      player_1.init_name("玛丽奥");
   
      Mario* player_2 = new Mario;
      player_2->init_name("路易基");
      
         cout<<"玩家1:";
      player_1.show_name();
      
         cout<<"玩家2:";
         player_2->show_name();
         
         system("pause>nul");
      return 0;
}

好了,一样完成功能了,而且看起来舒服很多了

等到日后,名字很长的时候,就会越发觉得这个符号用着舒服的

内容有点少啊,我再写点儿

可以看到之前我用了 init_name 来给类成员初始化内容了对吧,并且前一课也用了

所以这里我教点新的东西给你们

就是C++里的构造函数,是一个和类同名的函数,没有返回类型

这个函数是每一个类里都有的函数,每一个都有,包括刚才的马里奥类

我写一下

class Mario
{
public:
      Mario(){}       //构造函数,编译器给的函数就是这样的,啥也不干
}

当我们的类里没有构造函数的时候,也就是之前的几次代码。

编译器就会默认为我们隐含添加一个构造函数,然后这个构造函数啥也不干

然后当我们创建了这个类的对象时,就会自动运行一下这个函数,一定会运行,不用我们调用

给你们示范一下

class Mario
{
public:
      Mario()                //构造函数
      {
                cout<<"玛丽奥出现了"<<endl;
      }
};

int main()
{
      Mario player_1;
         
         system("pause>nul");
      return 0;
}

看到了吧,我们只是创建了这个对象,函数就执行了。

所以我们可以很方便的用这个函数给我们的类成员做初始化工作。

现在我把之前的代码再用构造函数重写一次

看代码

class Mario
{
private:
      string name;
public:
      Mario(const char* n)                //构造函数
      {
                name = n;
      }
      void init_name(const char* n)
      {
                name = n;
      }
      string get_name()                        //换了种写法,就别复制前面的了
      {
                return name;
      }
};

int main()
{
      /*在这里用构造函数初始化了一下成员变量name,大家看一下格式*/

      Mario player_1("玛丽奥");
         Mario* player_2 = new Mario("路易基");
         
         cout<<"玩家1:"<<player_1.get_name()<<endl;
         cout<<"玩家2:"<<player_2->get_name()<<endl;
         
         system("pause>nul");
      return 0;
}

代码写的很简洁了

那就基本上要结束了

在结束之前,再讲一个和构造函数相对的,就是析构函数

它会在对象被销毁之前执行,也就是结束的时候执行

和构造函数一样,如果我们不写,编译器就添加一个啥也不干的析构函数

上代码

class Mario
{
public:
      Mario()                //构造函数
      {
                cout<<"玛丽奥被创建了,游戏开始"<<endl;
      }

      ~Mario()      //析构函数,格式就是比构造函数多一个 " ~ "
      {
                cout<<"玛丽奥被销毁了,“哒哒哒~”,你挂了"<<endl;
      }
};

int main()
{
      Mario player_1;
         
         system("pause>nul"); //在这里停顿一下,按任意键后执行
      return 0;
}

会看到,最后,析构函数也执行了,说明我们已经运行到了main函数的末尾

大家应该还记得,函数结束后,会把自己申请的所有栈空间变量删除

所以,析构函数在被删除之前,执行了。

那如果是堆呢

int main()
{
      Mario* player_2 = new Mario;
         system("pause>nul");
      return 0;
}

如果我们不写delete

就会发现,析构函数没执行,也就是说,这个对象没有被删除,一直留在内存里,然后我们再也没法删了它了(想删掉它么?关机吧!)

现在加一句

int main()
{
      Mario* player_2 = new Mario;
         system("pause>nul");
      delete player_2;                //在这里销毁它,销毁前会运行一次析构函数
      return 0;
}

OK 讲解完毕,文字多了点,但是内容不多,就两点:
创建堆空间变量
构造/析构函数

慢慢看吧

dxxds 发表于 2013-3-19 08:31:47

给你的故事,C++的兴趣入门 11

本帖最后由 dxxds 于 2013-11-23 12:13 编辑

感觉最近更新的几次帖子的趣味性略有降低了
其实这和我最近的工作心态有关啊,过的不是很顺的时候,人就容易发挥失常啊……
感情也出现了问题,倒不是说哥和妹子出现危机,而是我一直单身……
像我这样从小玩游戏,生化危机各种过的人来说,现在玩个什么单身危机那也是很正常的了……

算了,这都是小事,不去管它。

前几篇内容相对枯燥并且在第十篇来了一个超大篇幅超高攻击的持久攻击技能,相信各位都有点虚了
所以秉着对各位的歉意,我决定放一篇更长的主动AOE技能来满足各位的心理需要和生理需要。

现在进入正题:创造游戏之初的人物职业选择部分

话说,我曾经对各种网游的总结,那就是:最终都是拼宝石……
并一直信誓旦旦的讲给身边的哥们儿,让他们给我一个游戏宗师的称号什么的

不过现在我顿悟了,那都是错的
网游的最终是什么?穿衣服!就是你能看到我也能看到的那一层在现实里不算是布料的布料

什么属性,耐久,那都是假的,最终满足我的,一定是那最漂亮的几套,最漂亮的,一定是那属性最好的

几套……
并且我们会在一开始的时候,就选好了那几套
为什么?
因为我们已经玩了很多遍,熟得不能再熟了
在职业确定下来的那一刻,衣服也就确定了,你穿或不穿,它们就在那,不离不弃。

于是我们在游戏里定义了一个日后会由我们操控的角色类,它会是什么职业(电脑)还不知道

class role      //角色
{
public:
    int LV;      //等级
    int HP;      //血值
    int AT;      //攻击力
    void show_state(){cout<<"等级:"<<LV<<"血量:"<<HP<<"攻击力:"<<AT<<endl<<endl;}
};

以后它是啥职业咱不管,但是这些属性是一定跑不掉的(目前有三个,但是真实游戏里会有很多很多)

然后我们再把其它类(职业)也写上,然后重用这些属性,也就是说我们再写具体职业的时候,这三个属

性我们就不写了,直接拿来用,怎么做?

像这样,我写两个职业,战士和法师

class warrior : public role    //战士,继承了角色类
{
public:
    warrior(){LV=1; HP=100; AT=10; cout<<"初始化了战士属性"<<endl;}
};

class magician : public role    //法师,继承了角色类
{
public:
    magician(){LV=1; HP=50; AT=20; cout<<"初始化了法师属性"<<endl;}
};

是public继承,以后都这么写private什么的继承很少很少能用到,继承就是说它拥有父类(role)的所有

public和protected的变量和函数。

那private里的变量和函数会怎么样,篇幅有限,我简短说明

假设有个父类是父亲类,里面有属性是:年龄。身高,胡子
有个继承类是女儿类,就要继承父类的属性,但是我显然不想让女儿继承胡子这个属性
所以我可以把胡子属性放到private里,这样,女儿就不会长胡子了,然后游戏后续可能有刮胡刀什么的

就会因为女儿没有胡子属性而失灵什么的,你们自己搞

好,那现在回来,说完继承,我们看看效果
在main里创建战士和法师

int main()
{   
    role* player = new warrior;    //创建了战士的资源
    player->show_state(); //显示战士属性
    delete player;      //删除战士资源
   
    player = new magician;    //创建了法师的资源
    player->show_state(); //显示法师属性
    delete player;      //删除法师资源
   
   system("pause>nul"); return 0;
}

都看懂了吧?那这一讲结束了

看完帖子的都不给哥回复啊混蛋!!!

dxxds 发表于 2013-4-20 10:03:20

本帖最后由 dxxds 于 2013-4-20 10:07 编辑

给你的故事,C++的兴趣入门 12

感觉之前的都写的太长了,会有很多人看不下去,所以这次我写短,希望能有人继续关注

上一节课已经写了职业的具体分工实现,那有了职业,咱就要给各个职业加上不同的技能,不然你可不会玩它

class role      //角色
{
public:
    int LV;      //等级
    int HP;      //血值
    int AT;      //攻击力
    void show_state();
    virtual ~role(){ cout<<"删除了role资源"<<endl;};//虚函数,为了能执行子类析构函数,顺利删除资源而写。这句会被子类析构函数覆盖,实际上不会执行
    virtual void skill()=0;      //虚函数,为了能调用子类的同名函数
};

void role::show_state()
{
    cout<<"等级:"<<LV<<"血量:"<<HP<<"攻击力:"<<AT<<endl<<endl;
}

class warrior : public role    //战士,继承了角色类
{
public:
    warrior()
    {
      LV=1; HP=100; AT=10; cout<<"初始化了战士属性"<<endl;
    }
    ~warrior()
    {
      cout<<"删除了战士资源"<<endl<<endl;
    }
    void skill()
    {
      cout<<"战士技:劈砍!"<<endl;
    }
};

class magician : public role    //法师,继承了角色类
{
public:
    magician()
    {
      LV=1; HP=50; AT=20; cout<<"初始化了法师属性"<<endl;
    }
    ~magician()
    {
      cout<<"删除了法师资源"<<endl<<endl;
    }
    void skill()
    {
      cout<<"法师技:冰箭!"<<endl;
    }
};

int main()
{
    role* player = new warrior; //role变成了战士
    player->skill(); //自然这里就是战士的技能
    delete player;//删了它,接下来让它变成法师

    player = new magician; //变成了法师
    player->skill(); //自然这里就是法师的技能
    delete player; //删了它 准备结束程序

    system(pause>nul);
    return 0;
}

可以看到我相对上一节课,只是在role类里多增加了一个skill的函数和一个~role的析构函数,并在之前加了一个virtual,表示这个函数是虚的。

这个虚函数类似一个JAVA里的interface,也就是接口。意思是让所有继承role的类,都必须实现这个函数,不然就报错

至于为什么要写这个函数?
因为role自己不知道将来在main里会变成战士还是法师,所以这里空下,交给子类实现。
但是这个函数必须要有,因为role不能调用子类有的而父类(也就是role自己)没有的函数方法

虚函数有两种,一种是可以自己(父类)实现的,还有一种是自己(父类)不能实现的

能实现的那种,前面加上“virtual”,然后直接写就行了。
不能实现的,前面加上“virtual”,后面加上 “=0”,就行了。

虚函数和一般函数的区别是啥?
区别就是,看下面这句
role* player = new warrior;
这句代码里有两个类,一个是role,一个是warrior
那如果这两个类里都有一个同名函数,那player执行哪个?

答案就是
如果函数是虚函数,就执行player子类的(warrior)
如果它不是虚函数,就执行player自己的(role)



OK 没啥难的吧?那就到这啦。

dxxds 发表于 2013-4-21 00:05:23

骷骷马力梦魇 发表于 2013-4-20 13:58 static/image/common/back.gif
char *who_is_the_most_handsome_man()
{
      return "dxxds!dxxds! I LOVE YOU !!!";


很细心,我漏了复制,没有把编译器里正确的代码复制过来

这个函数是要被cout调用的

main里改成

cout<<who_is_the_most_handsome_man()<<endl;

就好了

dxxds 发表于 2013-4-21 00:07:05

骷骷马力梦魇 发表于 2013-4-20 14:51 static/image/common/back.gif
我改出来了,,,应该这样
#include
#include


呵呵,是我漏写了

#46t

dxxds 发表于 2013-11-23 12:36:32

给你的故事,C++的兴趣入门 完结篇

本帖最后由 dxxds 于 2013-11-23 12:38 编辑

毕竟是入门教程,也不会讲的多深入了(模板和STL什么的另开贴讲)
我只是在这里讲了讲C++的大概语法,至于一些细节(有的也挺重要的),例如public或者private继承方式什么的等等
我就不讲了,主体学会了,这些细节你们自己找书看一下就会一下明白,我就不在这里费篇章了
而且C++细节也很多,我也讲不完

本来以为自己的教程会有很多篇,现在发现,似乎主体都讲的差不多了,那本节就不讲新东西了
用一个数据结构中最简单的结构作为本教程的结尾好啦
那就是~~~~超简单的动态链表!
链表作为C语言和C++都必须掌握的一部分内容被视为考试和面试强力杀招和法宝让无数新手头痛不已
所以本着救世主义精神和佛光普照的伟大行为为大家放出这段实际上是很初级的内容教程

《超级玛丽的奇幻冒险》之自创角色篇

那就上代码,这里依然写了超详尽的注释内容,让你一看就懂

#include <iostream>
using namespace std;

class DIY_Role
{
public:
      int ID;
      int HP;      //血量
      int AT; //攻击力
      DIY_Role* next_role;         //下一个角色
      DIY_Role()      //构造函数,就是对象被创建后自动运行的那玩意
      {
                next_role = NULL;      //弄成空,防止出意外
                HP = 0; AT = 0;      //初始化一下,写不写都行
                ++S_ID; ID = S_ID; //编号赋值
      }
      static int S_ID;      //静态 全局 唯一 用它给每一个对象的id赋值
};
int DIY_Role::S_ID = 0;

DIY_Role* head = NULL;      //头节点

DIY_Role* create_role()
{
      //目前 head p1 p2都是空的,就是说你一个角色都还没创建,所以现在得先创建一个,然后再循环创建
      //也就是说,创建分为两部分,分别是创建头节点,和创建后续节点
      DIY_Role* p1=NULL;      //本节点
      DIY_Role* p2=NULL;      //将要创建的下一个节点
      
      p2 = new DIY_Role;      //创建了一个了
      
      cout<<"请输入角色的血量(输入0表示停止创建)"<<endl;      //显然,角色的血量必须大于0
      cin>>p2->HP;
      if(p2->HP == 0)
      {
                delete p2;      //把刚才new出来的资源删掉
                p2 = NULL;//p2现在依然指向刚才new的地方,所以让它指回安全的地方去
                cout<<"创建结束"<<endl;
      }
      else
      {
                cout<<"请输入角色的攻击力"<<endl;      //攻击力可以为零和负数
                cin>>p2->AT;
      }
      head = p2;      //因为是第一个,head肯定指向它
      p1 = p2;      //p1自然也指向它
      //以上就把第一个节点建立好了,从第二个节点开始,循环建立就行了,上下的区别就是下面不在操作head
      
      while(true)
      {
                p1 = p2;      //把循环末尾创建的P2交给P1 然后P2创建新的
                p2 = new DIY_Role;
                cout<<"请输入角色的血量(输入0表示停止创建)"<<endl;      
                cin>>p2->HP;
                if(p2->HP == 0)
                {
                        delete p2;      
                        p2 = NULL;
                        break;
                }
                else
                {
                        cout<<"请输入角色的攻击力"<<endl;
                        cin>>p2->AT;
                        p1->next_role = p2;         //new出来的新交给本节点的下一个节点 用next_role记下地址
                }
      }
}

int main()
{
      create_role();
      return 0;
}

好详细的注释啊,我都觉得没啥好讲的了
你们自己运行一下,看看结果

显然现在我们是成功创建了3个角色

那我们看看它们

所以写一个show函数来显示

//显示一下你所创建的所有role
void show_role(DIY_Role* head)
{
      if( head!=NULL )
                for( DIY_Role* p = head; p != NULL ; p = p->next_role)
                {
                        cout<<"角色"<<p->ID<<" 血量:"<<p->HP<<" 攻击力:"<<p->AT<<endl;
                }
      else
         cout<<"没有角色啊 ╮(╯▽╰)╭"<<endl;
}

然后在main里用它

int main()
{
      create_role();
      show_role(head);
      return 0;
}

再看一次结果
那现在创建和遍历就都有了
下面再来一个查找

DIY_Role* find_role(DIY_Role* head,int f_id)
{
    DIY_Role* p1 = head;
    DIY_Role* p2 = p1; //p2表示目标节点 p1表示目标节点的父节点
   
    while(head!=NULL && p2->ID != f_id ) //寻找节点,一直找到末尾
    {
      p1 = p2;
      if(p2->next_role != NULL)
            p2=p2->next_role;
      else
            break;      //说明p2->next_role是空的 也就是找到末尾了
    }
    if(p2->ID == f_id) //说明找到了
    {
      cout<<"角色"<<p2->ID<<" 血量:"<<p2->HP<<" 攻击力:"<<p2->AT<<endl;
    }
    else
    {
      cout<<"没找到"<<endl;
      p1 = NULL; //没找到就返回空吧
    }
    return p1;    //返回目标节点的父节点或者NULL
}

这样就有了一个查找函数

返回父节点是为了后续能有更多操作,目前用p->next来表示目标节点

现在按着增删改查的顺序慢慢加函数

该到“改”了

void updata(DIY_Role* head,int u_id)
{
    //为了简便,我就直接让数字自加1000了,懂得应用就行
    //实际上比较好的方法是用一个专门接受要修改的数字的函数来封装这个函数,我就不弄了
    //两部分,头节点和后续节点
    if(head!=NULL && head->ID == u_id)
    {
      head->HP += 1000;
      head->AT += 1000;
      return;
    }
    DIY_Role* p = find_role(head,u_id);
    if(p!=NULL)
    {
      p->HP += 1000;
      p->AT += 1000;
    }
}

最后就是删了,这个稍微注意一下参数部分的写法

void delete_role(DIY_Role*& head,int d_id)
{
    //删除也分为两部分 删头节点和删后续节点
    DIY_Role* p = head;
    if(head!=NULL && head->ID == d_id)
    {
      head = head->next_role;
      cout<<"id:"<<head->ID<<endl;
      delete p;
      return;    //强制结束此语句适用于void函数
    }
    //删后续节点
    p = find_role(head,d_id);
    if( p ) // p!=NULL
    {
         DIY_Role* p2 = p->next_role; //用一个临时指针保存一下目标节点
      p->next_role=p2->next_role; //把目标节点的下一个节点交给父节点,保持链表连续
      delete p2; //可以删了目标节点了
      p2 = NULL; //这个不写也行
      cout<<"OK~ 删掉了"<<endl;   
    }
    else
    {
      cout<<"没找到要删的角色"<<endl;    // ╮(╯▽╰)╭
    }
}

参数部分我又用了*又用了&
表示我需要修改全局head本身

好了,烂代码就这么多
这代码的确很烂,比教科书的要差
但是烂有烂的好处,就是写的够直接,我尽一切可能去掉了不必要的旁支写法,基础差的人也能看懂。

那, 本入门教程就到此结束了

moxiagy 发表于 2013-1-3 14:57:00

#31x 反正我是吓退了。。

ID:L1 发表于 2013-1-3 15:00:34

无聊了继续学

_Nozomi 发表于 2013-1-3 16:14:36

已经合并 更新在自己这里回帖就好了

_Nozomi 发表于 2013-1-3 16:44:52

再说一次,更新请在这里回帖更新同样的主题不要另开新帖

jinlei6394 发表于 2013-1-3 18:33:32

习惯指针以后就受不了那些没有指针的语言了。
真是不自由啊

dxxds 发表于 2013-1-3 18:53:13

本帖最后由 dxxds 于 2013-1-11 14:02 编辑

Whisper1166 发表于 2013-1-3 16:44 static/image/common/back.gif
再说一次,更新请在这里回帖更新同样的主题不要另开新帖

guominhangmu 发表于 2013-1-5 11:15:45

看看

卖的很欢乐 发表于 2013-1-6 21:27:57

快到碗里来

lschmmlove 发表于 2013-1-6 22:14:17

来学习学习Y((^~^))Y~

小G去美帝学EMBS 发表于 2013-1-7 04:24:57

高级了。。。

achoxu 发表于 2013-1-7 10:35:11

简单易懂~喜欢,楼主继续哦#24m

zhusy1994 发表于 2013-1-8 01:46:44

楼主幸苦了~#31t
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 【教】给你的故事小游戏,C++兴趣入门(完结)