第一桶 从C到C++ 第十一碗 老C初论对象模型 小P学习基于对象(之二)

     “老C,过来帮个忙!”小P叫到。
     “喔,什么事情?”老C正在惬意的喝着茶水消食,一边在起点上翻看水文,“怎么了?”一边说,一边将转椅拉到小P身边。
     “我不知道怎么初始化childList_……”小P指着代码道。
     “哦?我看看。”老C伸过脖子。

applegame.h:

#if !defined(APPLE_GAME_H_)
#define APPLE_GAME_H_

#include "childlist.h"

class AppleGame
{
public:
    void play();

private:
    enum {CHILDREN_NUM = 20U, KICK_OUT_NUM = 7U};

private:
    bool isGameOver() const;
    void doPlay();
    int lastChildSeatNum() const;

private:
    ChildList childList_;
};

#endif // APPLE_GAME_H_


     “呵呵,你居然知道使用enum hacker这个惯用法,不错啊。”老C表扬小P,“那么问题在什么地方?”
     “我不知道如何初始化childList_,我认为可以将 CHILDREN_NUM 作为childList_的初始化参数,但是不知道在哪里调用……”小P指着代码,“因为我认为ChildList需要这样的构造函数—— ChildList(int childNum)。”
     “哦,这里你需要使用initialize list。”老C回答,然后接过键盘,进行了如下修改。

applegame.h:

#if !defined(APPLE_GAME_H_)
#define APPLE_GAME_H_

#include "childlist.h"

class AppleGame
{
public:
    AppleGame();

    void play();

private:
    enum {CHILDREN_NUM = 20U, KICK_OUT_NUM = 7U};

private:
    bool isGameOver() const;
    void doPlay();
    int lastChildSeatNum() const;

private:
    ChildList childList_;
};

#endif // APPLE_GAME_H_

------------------------------------------------------(朴实的分割线)

applegame.cpp:

#include "applegame.h"

//////////////////////////////////////////////////////////////////////////
// Public

AppleGame::AppleGame()
: childList_(CHILDREN_NUM)
{
}

     “看,这样就可以了。”老C解答到。“这个是关于构造函数的一些问题,你先继续,等程序完成了我们再来讨论有关问题。”
     “好的。”小P回答,然后接着写下如下代码。

applegame.h:

#if !defined(APPLE_GAME_H_)
#define APPLE_GAME_H_

#include "childlist.h"

class AppleGame
{
public:
    AppleGame();

    void play();

private:
    enum {CHILDREN_NUM = 20U, KICK_OUT_NUM = 7U};

private:
    bool isGameOver() const;
    void doPlay();
    int lastChildSeatNum() const;

private:
    ChildList childList_;
};

#endif // APPLE_GAME_H_

------------------------------------------------------(朴实的分割线)

applegame.cpp:

#include "applegame.h"
#include "mydebug.h"

#include <iostream>

//////////////////////////////////////////////////////////////////////////
// Public

AppleGame::AppleGame()
: childList_(CHILDREN_NUM)
{
}

void AppleGame::play()
{
    using namespace std;

    MY_DEBUG("Start playing game...\n");

    while (!isGameOver())
    {
        doPlay();
    }

    cout << "The last child's seat number is: " << lastChildSeatNum() << endl;
}


//////////////////////////////////////////////////////////////////////////
// Private

bool AppleGame::isGameOver() const
{
    return 1 == childList_.size();
}

void AppleGame::doPlay()
{
    MY_DEBUG("Playing game.\n");

    childList_.countOn();
    if (KICK_OUT_NUM == childList_.currNum())
    {
        childList_.removeCurrChild();
    }
    else
    {
        childList_.forward();
    }
}

int AppleGame::lastChildSeatNum() const
{
    int seatNum = childList_.lastChildNum();
    return seatNum;
}

------------------------------------------------------(朴实的分割线)

childlist.h:

#if !defined(CHILD_LIST_H_)
#define CHILD_LIST_H_

class ChildList
{
public:
    ChildList(int childNum);

    int size() const;
    void countOn();
    int currNum() const;
    void removeCurrChild();
    void forward();
    int lastChildNum() const;
};

#endif // CHILD_LIST_H_

------------------------------------------------------(朴实的分割线)

childlist.cpp:

#include "childlist.h"

#include "mydebug.h"

//////////////////////////////////////////////////////////////////////////
// Public

ChildList::ChildList( int childNum )
{
    MY_DEBUG_1("Create child list of %d children.\n", childNum);
}

int ChildList::size() const
{
    static int n = 10;

    return --n;
}

void ChildList::countOn()
{
    MY_DEBUG("Count on...\n");
}

int ChildList::currNum() const
{
    static int n = 0;

    return ++n;
}

void ChildList::removeCurrChild()
{
    MY_DEBUG("Remove current child from child list, then move to next child.\n");
}

void ChildList::forward()
{
    MY_DEBUG("Move to next child.\n");
}

int ChildList::lastChildNum() const
{
    return 10;
}

     “唔,照着你说的方法,我先写applegame.cpp,感觉需要childList_提供什么接口的时候,就在childlist.h中声明什么接 口。然后再加入childlist.cpp,简单的实现childlist.h中声明的接口,并加入调测代码……编译后修改了几处笔误,就是现在这个结 果。”小P揉揉手指,叫过正在天涯上灌水的老C,对他说道。
     “那么你认为程序执行的结果如何?”老C问道。
     “嗯,还行。起码证明了我的算法基本上是没有问题的。”小P回答。
     “那么你就打个版本标签吧……不明白?就是建立一个目录然后将代码拷贝过去。”老C心想是不是该给小P讲讲配置管理相关的事情了。
     “哦……”小P有样学样的建立了一个AppleGame_V3.01的目录,然后将所有代码拷贝了过去。
     “好吧,现在你再接着实现child list模块吧,有问题叫我,”老C说道,“我还和朋友有一些重要的事情商量……”于是他又跑去灌水了。
    
     老C先是灌了几篇水文,又在起点上看了积攒起来的几篇更新,然后又在qq上同mm聊了会天,看看表已经差不多2个小时了,扭过头看看小P,好像还在键盘上敲敲打打。“如何?”他问道。
     “嗯,我已经写完了,正在检查和调试……还根据反馈的信息修改了apple game模块和child list模块的某些代码。”小P答道。
     “哦?不错不错,比我想象的要快。”老C称赞道,“要不我们来一起看看?”
     “好!”小P应道。

applegame.cpp:

#include "applegame.h"
#include "mydebug.h"

#include <iostream>

//////////////////////////////////////////////////////////////////////////
// Public

AppleGame::AppleGame()
: childList_(CHILDREN_NUM)
{
}

void AppleGame::play()
{
    using namespace std;

    MY_DEBUG("Start playing game...\n");

    while (!isGameOver())
    {
        doPlay();
    }

    cout << "The last child's seat number is: " << lastChildSeatNum() << endl;
    cout << "Game Over! \n\n" << endl;
}


//////////////////////////////////////////////////////////////////////////
// Private

bool AppleGame::isGameOver() const
{
    MY_DEBUG_1("The game has %d child now.\n", childList_.size());
    return 1 == childList_.size();
}

void AppleGame::doPlay()
{
    MY_DEBUG("Playing game.\n");

    childList_.countOn();
    if (KICK_OUT_NUM == childList_.currNum())
    {
        childList_.resetCountNum();
        childList_.removeCurrChild();
    }
    else
    {
        childList_.forward();
    }
}

int AppleGame::lastChildSeatNum() const
{
    int seatNum = childList_.lastChildNum();
    return seatNum;
}

------------------------------------------------------(朴实的分割线)

childlist.h:

#if !defined(CHILD_LIST_H_)
#define CHILD_LIST_H_

#include "list.h"

class ChildList
{
public:
    ChildList(int childNum);

    int size() const;
    void countOn();
    int currNum() const;
    void resetCountNum();
    void removeCurrChild();
    void forward();
    int lastChildNum() const;

private:
    int currCountNum_;

    List chList_;
};

#endif // CHILD_LIST_H_

------------------------------------------------------(朴实的分割线)

childlist.cpp:

#include "childlist.h"

#include "mydebug.h"
#include "list.h"
#include <cassert>

#include "list.h"

//////////////////////////////////////////////////////////////////////////
// Public

ChildList::ChildList( int childNum )
: currCountNum_(0)
{
    assert(0 != childNum);

    MY_DEBUG_1("Create child list of %d children.\n", childNum);

    for (int i = 0; i < childNum; ++i)
    {
        ListNode* newNode = new ListNode;
        newNode->content_.seatNum_ = i + 1;
        chList_.pushBack(newNode);
    }

    chList_.setIndex(chList_.begin());
}

int ChildList::size() const
{
    return chList_.size();
}

void ChildList::countOn()
{
    
    ++currCountNum_;
    MY_DEBUG_1("Count on...Current count number is %d.\n", currCountNum_);
}

int ChildList::currNum() const
{
    return currCountNum_;
}

void ChildList::resetCountNum()
{
    currCountNum_ = 0;
}

void ChildList::removeCurrChild()
{
    MY_DEBUG("Remove current child from child list, then move to next child.\n");


    chList_.setIndex(chList_.erase(chList_.index()));
}

void ChildList::forward()
{
    MY_DEBUG("Move to next child.\n");

    chList_.forward();
}

int ChildList::lastChildNum() const
{
    ListNode* iter = chList_.begin();

    return iter->content_.seatNum_;
}

------------------------------------------------------(朴实的分割线)

list.h:

#if !defined(LIST_H_)
#define LIST_H_

struct Child
{
    int seatNum_;
};

typedef Child LIST_CONTENT;

struct ListNode
{
    ListNode* prev_;
    ListNode* next_;

    LIST_CONTENT content_;
};

class List
{
public:
    List();
    ~List();

    int size() const;
    void pushBack(ListNode* newNode);
    void popFront();

    void setIndex(ListNode* iter);
    ListNode* index() const;

    ListNode* begin() const;
    ListNode* end() const;
    
    void insert(ListNode* iter, ListNode* newNode);
    ListNode* erase(ListNode* iter);

    void clear();

    void forward();

private:
    ListNode* list_;
    ListNode* index_;

    int size_;

};

#endif // LIST_H_

------------------------------------------------------(朴实的分割线)

list.cpp:

#include "list.h"

#include <cstring>

#include "mydebug.h"

//////////////////////////////////////////////////////////////////////////
// Public.

List::List()
: size_(0)
{
    list_ = new ListNode;

    list_->prev_ = list_;
    list_->next_ = list_;
}

List::~List()
{
    clear();

    memset(list_, 0, sizeof(ListNode));

    delete list_;

    list_ = 0;
}

int List::size() const
{
    return size_;
}

void List::pushBack( ListNode* newNode )
{
    insert(end(), newNode);
}

void List::popFront()
{
    if (size())
    {
        erase(begin());
    }    
}

void List::setIndex( ListNode* iter )
{
    index_ = iter;
}

ListNode* List::index() const
{
    return index_;
}

ListNode* List::begin() const
{
    return list_->next_;
}

ListNode* List::end() const
{
    return list_;
}

void List::insert( ListNode* iter, ListNode* newNode )
{
    newNode->next_            = iter;
    newNode->prev_            = iter->prev_;
    newNode->prev_->next_    = newNode;
    newNode->next_->prev_    = newNode;

    ++size_;
}

ListNode* List::erase( ListNode* iter )
{
    MY_DEBUG_1("The node erased is %d.\n", iter->content_.seatNum_);
    
    ListNode* it = iter->next_;

    if (end() == it)
    {
        it = begin();
    }
    
    iter->prev_->next_ = iter->next_;
    iter->next_->prev_ = iter->prev_;

    memset(iter, 0, sizeof(ListNode));

    delete iter;

    --size_;    

    return it;
}

void List::clear()
{
    while (size())
    {
        popFront();
    }
}

void List::forward()
{
    index_ = index_->next_;

    if (end() == index_)
    {
        index_ = begin();
    }
}

     “嗯,很不错,”老C称赞道,“知道要使用initialize list初始化成员变量,知道要在构造函数中初始化linked list的head,还知道在析构函数中释放内存……”老C由衷的说,“你还是知道一些编程的技巧的啊。”
     “呵呵,以前本科的时候稍微学习过一些,哈哈。”小P谦虚道,“不过大部分代码还是参考你以前C版本的实现,我只不过是改良了一小部分而已。”
     “哦,不要小看改良的一小部分,如果在正确的道路上日积月累,这种变化累积起来效果还是很显著的。”老C道,“说说你经过编码和调试后,现在的感觉吧。”
     “嗯……我就说说问题吧——好的就不说了,都是相似的——首先,感觉这种编码方式有些繁杂,需要在几个文件中跳来跳去的;其次,我在编写list模块的时 候感觉index是个很烦人的东西,恨不得将它变为public的……目前的体会也就这么多。”小P一边想一边说道,“噢,对了,还有关于list,怎么 样才可以写好这个类呢?”小P又补充道。
     “你体会的挺深刻的啊,”老C点点头,“你说的没有错,如果照这种方式写代码的确有些繁杂,因为这个工作不应当直接在编码过程中进行,而应当在设计过程中 进行。要解决这个问题,需要引入新的工具——UML……等会再给你解释什么是UML……”制止住小P的发问,老C接着说,“确实,将index放在 list内部会造成种种不便,但是也有解决之道,比较经典的做法是将index从list中拿出来单独成为一个模块或class,这就是iterator 设计模式,我们以后再慢慢说……”他停顿了一下,“至于怎么良好的设计list,也有一些经验可以总结,同时也有一些业内的惯用法,这个我们接下来会更早 讨论这个问题。”
     “噢?那么我应当先学习什么呢?”小P问。
     “嗯,先接触一下UML,”老C想想说道,“然后我给你讲讲一些关于线性表的惯用法或者习语。最后我们再来讨论如何将index与它的操作从list中拿出来。”
     “好哩。”小P高兴的说道。
     “呵呵,现在我们终于有了一个用C++实现的稳定版本,我们就叫它4.0吧。”老C建议。
     “好啊。”小P同意,于是他建立了一个AppleGame_V4.0的目录,将所有代码拷贝进去。
     “现在我们进入到C++编程学习的阶段,”老C道,“你要试着从C过渡到C++,在转换过程中有一些细节上的事情需要注意一下。”
     “哦?哪些事情?”小P问。
     “包括以const、enum和inline替换#define,尽量使用const等等细节,我强烈建议你看看《Effective C++ Third Edition》的第一章,里面有很详细的介绍,不过在此之前我根据我们的代码简单的提示一下。”老C回答道,“这本书后面的章节可能有些深入,如果你不 想看也无所谓,反正还不是时候。”他又补充了一句。
     “好,我仔细研究一下那个什么书……”小P回答。
     “囧。”决定无视这个什么都不知道的小正太的天真问题,老C直接指着代码问道,“你知道这声明最后的const是什么含义吗?”

class AppleGame
{
public:
    AppleGame();

    void play();

private:
    enum {CHILDREN_NUM = 20U, KICK_OUT_NUM = 7U};

private:
    bool isGameOver() const;
    void doPlay();
    int lastChildSeatNum() const;

private:
    ChildList childList_;
};

     “因为isGameOver()函数没有对AppleGame类中的任何数据进行操作,所以将它声明为const,怎么?有什么问题吗?”
     “没有,我只是想和你详细讨论一下。在这isGameOver()被声明为const是很好的,但是我想说明一下背后发生了什么。”老C又拉过白板,指挥小P在上面擦出一块空白区域,然后写下几行文字。

C++:
class AppleGame
{
public:
    ...
private:
    bool isGameOver() const;
    ...
};

用C翻译:
struct AppleGame
{
public:
    ...
private:
    ...
};

bool AppleGame::isGameOver(const AppleGame* this);

     “这下你明白函数声明后面的const是什么意思了吧?”老C问。
     “哦,这样看来就清楚多了,怪不得isGameOver()函数不能对AppleGame类中的数据进行操作,原来是这么回事。”小P道。
     “呵呵,在这里写这段代码是想提醒你尽量使用const,并且要确保const的正确性,因为不声明为const,就是你认为不需要const。”老C接着问道,“你可以简单的解释一下const吗?”
     “就是表示常量……”
     “嘿,业余了吧。”还没有等到小P说完,老C就打断了他,“其实const表示的是只读,而不完全是常量!”
     “哦……呵呵,现在我知道了,也算变得职业一些了吧……对了,周末说说那个什么UML如何?反正快国庆了,趁着假期可以看看。”小P道。
     “哦?你国庆不陪女朋友出去玩玩?据我所知你女朋友很漂亮的,要知道女人是要经常陪的……”老C开始给小P介绍自己的惨痛经验。
     “哈哈,合理安排时间,相互不冲突,不冲突……”小P笑着回答。
     两个人说说笑笑,收拾东西,离开了教研室。

(下面就进入C++阶段啦,请关注第二桶 基于对象)


posted on 2009-02-20 20:02 Anderson 阅读(1577) 评论(4)  编辑 收藏 引用

评论

# re: 第一桶 从C到C++ 第十一碗 老C初论对象模型 小P学习基于对象(之二) 2009-02-20 22:11 ry

高手,有空去我网站做作
http://www.571sd.cn
你的建站专家  回复  更多评论   

# re: 第一桶 从C到C++ 第十一碗 老C初论对象模型 小P学习基于对象(之二)[未登录] 2009-02-22 10:11 笨笨

写得不错啊,很深刻。
适合于已经学习了C语言,对C++也有些了解的人读  回复  更多评论   

# re: 第一桶 从C到C++ 第十一碗 老C初论对象模型 小P学习基于对象(之二)[未登录] 2009-02-22 11:27 崔友志

循序渐进 很有条理  回复  更多评论   

# re: 第一桶 从C到C++ 第十一碗 老C初论对象模型 小P学习基于对象(之二) 2009-02-23 17:57 崇拜阿

崇拜阿,什么出书?  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2009年2月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
1234567

导航

统计

常用链接

留言簿(6)

随笔档案(21)

文章档案(1)

搜索

最新评论

阅读排行榜

评论排行榜