http://m.shnenglu.com/Tveiker/archive/2011/01/08/138154.aspx本鏈接是“隨筆”寫的一篇名叫《鏈表實(shí)驗(yàn)》的文章,以下是我的分析。
我理解你說的學(xué)號(hào)的問題是:一個(gè)叫X是男性年齡是22歲的學(xué)生,假設(shè)其學(xué)號(hào)是2009000,把該學(xué)生加入到鏈表之后,該學(xué)生的
學(xué)號(hào)發(fā)生了變化,不再是2009000。
如果你所說的學(xué)號(hào)問題和上面的描述一致的話,該問題產(chǎn)生的原因是:Student類不應(yīng)該有拷貝構(gòu)造函數(shù)和賦值函數(shù)。
假如為了實(shí)現(xiàn)Student類與STL容器搭配使用的能力(要獲得此種能力Student必須有拷貝構(gòu)造函數(shù)和其他一些函數(shù)根據(jù)你所使用到的容器
和算法所要求的特殊函數(shù),例如operator<)的話,這兩個(gè)函數(shù)也應(yīng)該換種寫法,為了實(shí)現(xiàn)此種能力這兩個(gè)函數(shù)有以下實(shí)現(xiàn)方法:
(1)學(xué)號(hào)的轉(zhuǎn)移,參考STL的auto_ptr的控制權(quán)轉(zhuǎn)移策略
以上是對(duì)你碰到問題的說明,另外從你所寫的代碼中還發(fā)現(xiàn)了一些問題和不足之處,具體詳述如下:
問題
(1)Student的構(gòu)造函數(shù)(Student()、Student(char *name,char* sex,int age))沒有把next初始化
(2)Student的拷貝構(gòu)造函數(shù)沒有進(jìn)行if (this != &s)的判斷
(3)Student類中的Name、Sex可以不使用char*類型(之所以把這個(gè)情況說成是問題而不是不足之處是為了以后寫代碼是少犯與指針相關(guān)的錯(cuò)誤)
(4)在Student類的析構(gòu)函數(shù)中不能對(duì)寫“Stu_no--;”這樣的語句,如果想實(shí)現(xiàn)學(xué)號(hào)重用的能夠的話就需要記錄學(xué)號(hào)的使用情況(本文不討論此問題)
不足之處
(1)形如class Student{
的代碼風(fēng)格不好,應(yīng)該修改為
class Student
{
(2)學(xué)號(hào)不應(yīng)該是一個(gè)整數(shù)并且應(yīng)該隱藏學(xué)號(hào)這一重要對(duì)象的生成方式(因?yàn)閷W(xué)號(hào)很可能每個(gè)學(xué)校不一樣,為了應(yīng)對(duì)變化,所以對(duì)可能發(fā)生變化
的地方進(jìn)行封裝)
(3)Student類不應(yīng)該包含next指針
(4)Link類中的Add函數(shù)的接口設(shè)計(jì)很不合理(在使用的時(shí)候需要先構(gòu)造出一個(gè)Student,這很沒有必要)
(5)Link類中的pivot作為類的成員變量不太合適并且沒有多大意義
提醒你一下:請(qǐng)仔細(xì)思考拷貝構(gòu)造函數(shù)的意義,拷貝構(gòu)造的意義簡(jiǎn)單來說就是克隆,而你寫的代碼沒有克隆好,所以的導(dǎo)致了學(xué)號(hào)問題
針對(duì)上述所有問題的修改方案有三種
(1)A方案:禁止拷貝構(gòu)造
(2)B方案:學(xué)號(hào)轉(zhuǎn)移 非法學(xué)號(hào)
各個(gè)方案的實(shí)現(xiàn)方法如下,其中對(duì)于學(xué)號(hào)的生成方法這二種方案都一樣(為了簡(jiǎn)單起見,學(xué)號(hào)仍然采用整數(shù))
1
/**//************************************************************************/
2
/**//* 學(xué)號(hào)生成 */
3
/**//************************************************************************/
4
5
typedef int StudentID;
6
7
class StudentIDGenerate
8

{
9
public:
10
static StudentID GenerateID()
11
{
12
return StudentBaseID++;
13
}
14
15
private:
16
static StudentID StudentBaseID;
17
};
18
19
StudentID StudentIDGenerate::StudentBaseID = 2009000;
20
21
/**//************************************************************************/
22
/**//* 鏈表節(jié)點(diǎn) */
23
/**//************************************************************************/
24
template <typename T>
25
struct 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
5
class Student
6

{
7
friend class Link;
8
9
public:
10
Student();
11
Student(string const &strName,string const &strSex, int nAge);
12
~Student();
13
14
public:
15
void display();
16
void SetValue(string const &strName,string const &strSex, int nAge);
17
int GetAge() const;
18
19
private://通過把以下函數(shù)設(shè)置為私有來實(shí)現(xiàn)禁止拷貝構(gòu)造的能力
20
Student(const Student &);
21
Student &operator=(const Student &s);
22
23
private:
24
string m_strName;
25
int m_nAge;
26
string m_strSex;
27
StudentID m_ID;
28
};
29
30
typedef LinkNode<Student> StudentNode;
31
32
Student::Student()
33

{
34
m_ID = StudentIDGenerate::GenerateID();
35
m_nAge = 0;
36
}
37
38
Student::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
46
Student::Student(const Student &s)
47

{
48
assert(false);
49
}
50
51
Student &Student::operator =(const Student &s)
52

{
53
assert(false);
54
return *this;
55
}
56
57
Student::~Student()
58

{
59
60
}
61
62
void Student::display()
63

{
64
cout<<"Name is:"<<m_strName<<" ID is:"<<m_ID<<" Sex is:"<<m_strSex<<" Age is:"<<m_nAge<<endl;
65
}
66
67
void 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
74
int Student::GetAge() const
75

{
76
return m_nAge;
77
}
78
79
class Link
80

{
81
public:
82
Link();
83
~Link();
84
85
public:
86
void Delete(StudentID);
87
void Add(string const &strName,string const &strSex,int nAge);
88
void Display();
89
90
private:
91
StudentNode *m_pHead;
92
StudentNode *m_pTail;
93
};
94
95
Link::Link()//構(gòu)造空鏈表
96

{
97
m_pHead=NULL;
98
m_pTail=NULL;
99
}
100
101
Link::~Link()//釋放內(nèi)存
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
111
void Link::Add(string const &strName,string const &strSex,int nAge)//向鏈表中添加學(xué)生
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
129
void Link::Display()//顯示鏈表中學(xué)生信息
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
141
void Link::Delete(int nAge)//刪除鏈表中所有年齡為nAge的學(xué)生
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
}
以下是測(cè)試方案A的代碼
void main()


{
//測(cè)試零
//vector<Student> vecStu;
//Student st1;
//vecStu.push_back(st1);

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

//測(cè)試一
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();

//測(cè)試二
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();

//測(cè)試三
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();

//測(cè)試四
Link link3;
link3.Add("X","Boy",22);
link3.Display();
link3.Delete(22);

link3.Display();

//測(cè)試五
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是測(cè)試零下面所注釋掉的代碼不能通過編譯,在VC2008的IDE下會(huì)報(bào):class“Student”: 沒有可用的復(fù)制構(gòu)造函數(shù)或復(fù)制構(gòu)造函數(shù)聲明為“explicit”錯(cuò)誤
為了修正此錯(cuò)誤,對(duì)代碼就行了修改,也就是B方案
const StudentID ErrorStudentID = 0;//其實(shí)非法學(xué)號(hào)是個(gè)范圍[負(fù)無窮,2009000] 添加的代碼
mutable StudentID m_ID;//為了能夠在拷貝構(gòu)造函數(shù)中修改該值,把其類型聲明為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:
使用面向?qū)ο笳Z言進(jìn)行程序設(shè)計(jì)的時(shí)候一個(gè)關(guān)鍵的問題是:設(shè)計(jì)一個(gè)好的類,這里的Student類就是一個(gè)典型的例子。
學(xué)習(xí)的時(shí)候當(dāng)然可以自己寫個(gè)鏈表,自己管理內(nèi)存;但是在實(shí)際工作中還是要多用STL現(xiàn)成的容器和算法,只有這樣才能提高代碼的質(zhì)量,降低錯(cuò)誤發(fā)生的概率。
上面代碼實(shí)際還可以繼續(xù)進(jìn)行改進(jìn),不夠?qū)懙来朔N程度基本上沒有什么大問題了。
posted on 2011-01-09 07:45
OnTheWay 閱讀(1623)
評(píng)論(2) 編輯 收藏 引用 所屬分類:
個(gè)人感悟