OnTheWay2012
埋葬昨天的我,迎来重生的我!
posts - 15,  comments - 89,  trackbacks - 0

http://www.cppblog.com/Tveiker/archive/2011/01/08/138154.aspx本链接是“随笔”写的一篇名叫《链表实验》的文章,以下是我的分析。

我理解你说的学号的问题是:一个叫X是男性年龄是22岁的学生,假设其学号是2009000,把该学生加入到链表之后,该学生的
学号发生了变化,不再是2009000。

如果你所说的学号问题和上面的描述一致的话,该问题产生的原因是:Student类不应该有拷贝构造函数和赋值函数。
 假如为了实现Student类与STL容器搭配使用的能力(要获得此种能力Student必须有拷贝构造函数和其他一些函数根据你所使用到的容器
 和算法所要求的特殊函数,例如operator<)的话,这两个函数也应该换种写法,为了实现此种能力这两个函数有以下实现方法:
 (1)学号的转移,参考STL的auto_ptr的控制权转移策略

 以上是对你碰到问题的说明,另外从你所写的代码中还发现了一些问题和不足之处,具体详述如下:
 问题
  (1)Student的构造函数(Student()、Student(char *name,char* sex,int age))没有把next初始化
  (2)Student的拷贝构造函数没有进行if (this != &s)的判断
  (3)Student类中的Name、Sex可以不使用char*类型(之所以把这个情况说成是问题而不是不足之处是为了以后写代码是少犯与指针相关的错误)
  (4)在Student类的析构函数中不能对写“Stu_no--;”这样的语句,如果想实现学号重用的能够的话就需要记录学号的使用情况(本文不讨论此问题)

 不足之处
  (1)形如class Student{
  的代码风格不好,应该修改为
  class Student
  {
  (2)学号不应该是一个整数并且应该隐藏学号这一重要对象的生成方式(因为学号很可能每个学校不一样,为了应对变化,所以对可能发生变化
    的地方进行封装)
  (3)Student类不应该包含next指针
  (4)Link类中的Add函数的接口设计很不合理(在使用的时候需要先构造出一个Student,这很没有必要)
  (5)Link类中的pivot作为类的成员变量不太合适并且没有多大意义

提醒你一下:请仔细思考拷贝构造函数的意义,拷贝构造的意义简单来说就是克隆,而你写的代码没有克隆好,所以的导致了学号问题
针对上述所有问题的修改方案有三种
(1)A方案:禁止拷贝构造
(2)B方案:学号转移 非法学号
各个方案的实现方法如下,其中对于学号的生成方法这二种方案都一样(为了简单起见,学号仍然采用整数)

 1/************************************************************************/
 2/*                          学号生成                                    */
 3/************************************************************************/
 4
 5typedef int StudentID;
 6
 7class StudentIDGenerate
 8{
 9public:
10    static StudentID GenerateID()
11    {
12        return StudentBaseID++;
13    }

14
15private:
16    static StudentID StudentBaseID;
17}
;
18
19StudentID StudentIDGenerate::StudentBaseID = 2009000;
20
21/************************************************************************/
22/*                            链表节点                                  */
23/************************************************************************/
24template <typename T>
25struct LinkNode
26{
27    T m_value;
28    LinkNode<T> *m_pNext;
29
30    LinkNode() : m_pNext(NULL)
31    {
32
33    }

34}
;
以上是通用代码,下面是方案A的代码
  1/************************************************************************/
  2/*                       A方案                                          */
  3/************************************************************************/
  4
  5class Student
  6{
  7    friend class Link;
  8
  9public:
 10    Student();
 11    Student(string const &strName,string const &strSex, int nAge);
 12    ~Student();
 13
 14public:
 15    void display();
 16    void SetValue(string const &strName,string const &strSex, int nAge);
 17    int GetAge() const;
 18
 19private://通过把以下函数设置为私有来实现禁止拷贝构造的能力
 20    Student(const Student &);
 21    Student &operator=(const Student &s);
 22
 23private:
 24    string m_strName;
 25    int m_nAge;
 26    string m_strSex;
 27    StudentID m_ID;
 28}
;
 29
 30typedef LinkNode<Student> StudentNode;
 31
 32Student::Student()
 33{
 34    m_ID = StudentIDGenerate::GenerateID();
 35    m_nAge = 0;
 36}

 37
 38Student::Student(string const &strName,string const &strSex,int nAge)
 39{
 40    m_strName = strName;
 41    m_strSex = strSex;
 42    m_ID = StudentIDGenerate::GenerateID();
 43    m_nAge = nAge;
 44}

 45
 46Student::Student(const Student &s)
 47{
 48    assert(false);
 49}

 50
 51Student &Student::operator =(const Student &s)
 52{
 53    assert(false);
 54    return *this;
 55}

 56
 57Student::~Student()
 58{
 59    
 60}

 61
 62void Student::display() 
 63{
 64    cout<<"Name is:"<<m_strName<<"    ID is:"<<m_ID<<"    Sex is:"<<m_strSex<<"    Age is:"<<m_nAge<<endl;
 65}

 66
 67void Student::SetValue(string const &strName,string const &strSex, int nAge)
 68{
 69    m_strName = strName;
 70    m_strSex = strSex;
 71    m_nAge = nAge;
 72}

 73
 74int Student::GetAge() const
 75{
 76    return m_nAge;
 77}

 78
 79class Link
 80{
 81public:
 82    Link();
 83    ~Link();
 84
 85public:
 86    void Delete(StudentID);
 87    void Add(string const &strName,string const &strSex,int nAge);
 88    void Display();
 89
 90private:
 91    StudentNode *m_pHead;
 92    StudentNode *m_pTail;
 93}
;
 94
 95Link::Link()//构造空链表
 96{
 97    m_pHead=NULL;
 98    m_pTail=NULL;
 99}

100
101Link::~Link()//释放内存
102{
103    while (NULL != m_pHead)
104    {
105        StudentNode *pTemp = m_pHead;
106        m_pHead = m_pHead->m_pNext;
107        delete pTemp;
108    }

109}
    
110
111void Link::Add(string const &strName,string const &strSex,int nAge)//向链表中添加学生
112{
113    if(m_pHead==NULL)
114    {
115        m_pHead = new StudentNode();
116        m_pHead->m_value.SetValue(strName, strSex, nAge);
117        m_pTail = m_pHead;
118        m_pTail->m_pNext = NULL;
119    }

120    else
121    {
122        m_pTail->m_pNext = new StudentNode;
123        m_pTail->m_pNext->m_value.SetValue(strName, strSex, nAge);
124        m_pTail->m_pNext->m_pNext = NULL;
125        m_pTail = m_pTail->m_pNext;
126    }

127}

128
129void Link::Display()//显示链表中学生信息
130{
131    cout<<endl;
132    StudentNode *pTemp = m_pHead;
133    while (NULL != pTemp)
134    {
135        pTemp->m_value.display();
136        pTemp = pTemp->m_pNext;
137    }

138    cout<<endl;
139}

140
141void Link::Delete(int nAge)//删除链表中所有年龄为nAge的学生
142{
143    StudentNode *pPre = m_pHead;
144    StudentNode *pCur = m_pHead;
145    while (NULL != pCur)
146    {
147        if (pCur->m_value.GetAge() == nAge)
148        {
149            if (pCur == m_pHead)
150            {
151                pPre = m_pHead = m_pHead->m_pNext;
152            }

153            else
154            {
155                pPre->m_pNext = pCur->m_pNext;
156            }

157            
158            pCur->m_value.display();
159            delete pCur;
160            pCur = NULL != pPre ? pPre->m_pNext : NULL;
161        }

162        else
163        {
164            pPre = pCur;
165            pCur = pCur->m_pNext;
166        }

167    }

168}
以下是测试方案A的代码
void main()
{
    
//测试零
    
//vector<Student> vecStu;
    
//Student st1;
    
//vecStu.push_back(st1);

    
//Student st2;
    
//vecStu.push_back(st2);

    
//测试一
    Link link;
    link.Add(
"X","Boy",22);
    link.Add(
"Y","Boy",20);
    link.Add(
"Z","Boy",21);
    link.Add(
"U","Girl",22);
    
    link.Display();
    link.Delete(
21);
    
    link.Display();

    
//测试二
    Link link1;
    link1.Add(
"X","Boy",22);
    link1.Add(
"Y","Boy",20);
    link1.Add(
"Z1","Boy",21);
    link1.Add(
"Z2","Boy",21);
    link1.Add(
"U","Girl",22);

    link1.Display();
    link1.Delete(
21);

    link1.Display();

    
//测试三
    Link link2;
    link2.Add(
"X","Boy",22);
    link2.Add(
"Y","Boy",20);
    link2.Add(
"Z1","Boy",21);
    link2.Add(
"Z2","Boy",21);
    link2.Add(
"U","Girl",22);

    link2.Display();
    link2.Delete(
22);

    link2.Display();

    
//测试四
    Link link3;
    link3.Add(
"X","Boy",22);
    
    link3.Display();
    link3.Delete(
22);

    link3.Display();

    
//测试五
    Link link4;
    link4.Add(
"X","Boy",22);
    link4.Add(
"Y","Boy",20);
    link4.Add(
"Z1","Boy",21);
    link4.Add(
"Z2","Boy",21);
    link4.Add(
"U","Girl",22);

    link4.Display();
    link4.Delete(
1);

    link4.Display();
}
其中采用方案A是测试零下面所注释掉的代码不能通过编译,在VC2008的IDE下会报:class“Student”: 没有可用的复制构造函数或复制构造函数声明为“explicit”错误

为了修正此错误,对代码就行了修改,也就是B方案
const StudentID ErrorStudentID = 0;//其实非法学号是个范围[负无穷,2009000]   添加的代码
mutable StudentID m_ID;//为了能够在拷贝构造函数中修改该值,把其类型声明为mutable  修改的代码

//修改的代码
Student::Student(const Student &s)
{
    m_strName 
= s.m_strName;
    m_strSex 
= s.m_strSex;
    m_ID 
= s.m_ID;
    s.m_ID 
= ErrorStudentID;
    m_nAge 
= s.m_nAge;
}


//修改的代码
Student &Student::operator =(const Student &s)
{
    
if (this != &s)
    
{
        m_strName 
= s.m_strName;
        m_strSex 
= s.m_strSex;
        m_ID 
= s.m_ID;
        s.m_ID 
= ErrorStudentID;
        m_nAge 
= s.m_nAge;
    }


    
return *this;
}

PS:
使用面向对象语言进行程序设计的时候一个关键的问题是:设计一个好的类,这里的Student类就是一个典型的例子。
学习的时候当然可以自己写个链表,自己管理内存;但是在实际工作中还是要多用STL现成的容器和算法,只有这样才能提高代码的质量,降低错误发生的概率。
上面代码实际还可以继续进行改进,不够写道此种程度基本上没有什么大问题了。
posted on 2011-01-09 07:45 OnTheWay 阅读(1592) 评论(2)  编辑 收藏 引用 所属分类: 个人感悟

FeedBack:
# re: 对“随笔”所写的一篇文章《链表实验》的一点看法
2011-01-09 11:17 | あ维wêiセ
谢谢咯,呵呵。我喜欢这代码风格,向你学习  回复  更多评论
  
# re: 对“随笔”所写的一篇文章《链表实验》的一点看法
2011-01-09 19:58 | 陈梓瀚(vczh)
我认为问题只需要修改成,只将某一个指定的vector<Student>里面的Student实例视为合法实例就好了。其他地方存放的Student都是非法的,但是不禁止复制,只是复制后改了也没用的意思。  回复  更多评论
  

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



<2010年3月>
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(4)

随笔分类

随笔档案

友情连接

搜索

  •  

最新评论

阅读排行榜

评论排行榜