大家好。我正在做一个涉及动态内存分配,指针,类和异常的链表练习。有人愿意批评它并告诉我我做错了什么以及我应该在风格和上面列出的那些主题方面做得更好吗?
/*
Linked List exercise
*/
#include <iostream>
#include <exception>
#include <string>
using namespace std;
class node{
public:
node * next;
int * data;
node(const int i){
data = new int;
*data = i;
}
node& operator=(node n){
*data = *(n.data);
}
~node(){
delete data;
}
};
class linkedList{
public:
node * head;
node * tail;
int nodeCount;
linkedList(){
head = NULL;
tail = NULL;
}
~linkedList(){
while (head){
node* t = head->next;
delete head;
if (t) head = t;
}
}
void add(node * n){
if (!head) {
head = n;
head->next = NULL;
tail = head;
nodeCount = 0;
}else {
node * t = head;
while (t->next) t = t->next;
t->next = n;
n->next = NULL;
nodeCount++;
}
}
node * operator[](const int &i){
if ((i >= 0) && (i < nodeCount)) throw new exception("ERROR: Invalid index on linked list.", -1);
node *t = head;
for (int x = i; x < nodeCount; x++) t = t->next;
return t;
}
void print(){
if (!head) return;
node * t = head;
string collection;
cout << "[";
int c = 0;
if (!t->next) cout << *(t->data);
else while (t->next){
cout << *(t->data);
c++;
if (t->next) t = t->next;
if (c < nodeCount) cout << ", ";
}
cout << "]" << endl;
}
};
int main (const int & argc, const char * argv[]){
try{
linkedList * myList = new linkedList;
for (int x = 0; x < 10; x++) myList->add(new node(x));
myList->print();
}catch(exception &ex){
cout << ex.what() << endl;
return -1;
}
return 0;
}
数据不需要成为指针。
使用ctor-initializer列表,它更适合const正确性和异常安全性。你的构造函数都不需要在体内有任何代码。
您的linkedList构造函数未初始化nodeCount。
您没有使用列表的尾部指针。它可以节省您在非空的添加情况下扫描整个列表 - 如果您保持最新。
索引(operator [])在链表上是一种不寻常的支持。 OTOH你还没有删除功能。
operator []不应该通过引用获取其参数。只有大型结构需要通过const引用传递,像int这样的小类型应该只是通过值传递。
现在,如果添加失败,指向新node()的指针会泄漏。 (但我实际上并没有看到添加失败的方法,除非列表链接被破坏。)
您应该在节点上定义私有拷贝构造函数以防止数据双重释放。每次定义operator =和析构函数时,您还应该定义或删除复制构造函数(三个规则)。您还应该在linkedList上定义私有拷贝构造函数和赋值运算符以防止双重释放。
不使用print函数中的变量字符串集合。 print()中的变量t应该是指向const的指针。 print本身应该是const成员函数。
/*
Linked List exercise
*/
class node {
public:
node * next;
int * data;
data
并不需要成为一个指针(除非你的课堂作业必须是这样)。您可以将其更改为int
,然后将引用从*(t->data)
更改为t->data
。同样,您不需要在ctor / dtor中使用new
和delete
。
node(const int i) {
data = new int;
*data = i;
}
node& operator=(node n) {
*data = *(n.data);
}
~node() {
delete data;
}
};
class linkedList {
public:
node * head;
node * tail;
int nodeCount;
linkedList() {
head = NULL;
tail = NULL;
您应该在此处初始化所有数据成员。 nodeCount
将有一些随机值,除非你在这里设置为零。
}
~linkedList() {
while (head) {
node* t = head->next;
delete head;
if (t)
head = t;
这看起来像一个bug。您需要始终分配head = t
,即使t是NULL
,否则您的循环将不会终止(但多次删除同一节点可能会导致异常)。
}
}
void add(node * n) {
if (!head) {
head = n;
head->next = NULL;
tail = head;
nodeCount = 0;
我认为整个if
声明是不必要的。当链表为空时,两个分支都执行相同的操作,因此您可以始终使用第二个(else)分支。
此外,最后将nodeCount
设置为0
似乎是一个bug;不应该是1
?
} else {
node * t = head;
while (t->next)
t = t->next;
t->next = n;
n->next = NULL;
nodeCount++;
}
}
node * operator[](const int &i) {
一个小的,但const int &
论点并没有任何意义。你没有通过传递int
类型获得任何东西。
if ((i >= 0) && (i < nodeCount))
throw new exception("ERROR: Invalid index on linked list.", -1);
上面的条件向后看我;你总是会抛出有效参数的异常。
node *t = head;
for (int x = i; x < nodeCount; x++)
t = t->next;
上面的循环对我来说似乎有点怀疑。如果i
是你想要的节点从零开始的索引,你的计数器不应该从0
到i-1
吗?
return t;
}
void print() {
if (!head)
return;
同样,你应该使这个方法工作,而不必防止空列表。我希望有一个空列表可以打印像[]
这样的东西,但是上面有防护代码,你什么都不打印。
node * t = head;
string collection;
int c = 0;
cout << "[";
if (!t->next) {
cout << *(t->data);
像上面的其他条件一样,我认为上面的分支是不必要的。第二个(else)分支应该处理NULL
案件就好了。
} else {
while (t->next) {
我想你想要while (t)
而不是while (t->next)
cout << *(t->data);
c++;
if (t->next)
t = t->next;
我认为这是一个像之前的错误。你应该总是分配't = t-> next',否则你的循环不会终止。
if (c < nodeCount)
cout << ", ";
}
}
cout << "]" << endl;
}
};
int main (const int & argc, const char * argv[]) { // const int& ?
const int &
再次......
try {
linkedList * myList = new linkedList;
for (int x = 0; x < 10; x++)
myList->add(new node(x));
myList->print();
} catch(exception &ex) {
cout << ex.what() << endl;
return -1;
}
对于家庭作业来说可能还不错,但是抓住每一个可能的例外(正如你上面所做的那样)可能并没有在这里增加太多价值,并且通常被认为是不好的做法。
如果省略上面的try / catch内容,我怀疑无论何时程序终止异常,编译器提供的默认顶级异常处理程序都会转储异常和堆栈跟踪信息(取决于你的编译器)。
return 0;
}
1)您可以使用data = new int(i)
初始化字段。
2)你在operator [] if ((i >= 0) && (i < nodeCount))
中创建了一个错误的条件,它应该被倒置,就像if ((i < 0) || (i >= nodeCount))
if ((i >= 0) && (i < nodeCount)) throw new exception("ERROR: Invalid index on linked list.", -1);
该行应为:
if( i < 0 || i >= nodeCount ) throw...
add方法也有问题:
void add(node * n){
if (!head) {
head = n;
head->next = NULL;
tail = head;
nodeCount = 0;
}else {
node * t = head;
while (t->next) t = t->next;
t->next = n;
n->next = NULL;
nodeCount++;
}
}
对于一个添加后的第一个节点,计数将为0,对于另一个,您正在进行搜索以插入链接列表...人们期望的操作是O(1)。
更典型的实现是:
void add( node* n ) {
node* old_head = head;
head = n;
head->next = old_head;
++nodeCount;
if( old_head == NULL )
tail = head
}