合并两个有序单链表,对象析构这一着我实在没想到。
编辑
最初代码中把b链表合并到a链表中后,b链表与a链表还没有断开。
两个链表的合并是调用函数进行的,函数的参数是一个链表类型(传值)。注意,虽然传值是在函数中建立了一个b链表的副本,但是,链表类的成员其实就只有头尾两个指针,作为链表实体的结点从来都只有一份。
那么问题来了,当函数调用介绍后,副本b这个实例就要析构了!从b链表的头结点开始往后面把内存释放得干干净净。好不容易合并出来的新a链表就跟着寄了。
a链表寄了之后,再到主函数里输出a链表,a链表并不会简单地就输出前面一段然后让你看着你的程序正常结束。(图3)虽然从2结点到后面都释放了,但是,1结点里面还存着2结点的地址。内存是释放了,但是程序交出去的只是访问权限,地址还是原来的地址没有变,而结构体的成员变量标识符已经对该地址没有指导作用。(比如结点node的data变量,如果你对一个失去权限的地址使用node->data访问,说实话,我也不知道它会访问那个地址处的哪一段字节)。
程序运行可能结果:
1)你的编译器有可能不会限制这种非法访问权限,于是,在访问1结点之后,继续访问已经不存在的2结点,输出一个莫名其妙的数字。令我感到奇怪的是,访问2之后程序并不停下来(可能因为我设置的循环退出条件是指针到NULL,而随便取一段字节大概率不是0),而是像个死循环一直输出莫名其妙的数字。
2)有时你的编译器又会阻止你,比如我的dev-c++5.11后来程序可以停下来,只是不能在主函数里输出a链表,然后return一个非0数字。
3)有时你的编译器里运行代码甚至可能没有任何异常,它可以正常给你输出a链表,然后正常给你return 0;
不管编译器怎么做,总之代码确实是有问题的。
后来我一个解决尝试是,在函数最后将b2指向b链表的头结点,将头节点的next置空,断开和a链表的连接。但是问题没有解决,因为b链表的头结点会在b的副本析构时释放掉。释放掉又怎么样呢?反正这个结点没有用了。但是,在main函数运行结束后,b链表作为一个实例也是要析构的,实例b的头指针里依然存着原来头结点的地址,而这个头结点其实已经被释放掉了。于是,在析构b时,非法访问又发生了。
于是我尝试将b2也置为空,后来我意识到没有用,因为b2只是被我指向了b的头结点,将b2置空只是不指向b的头结点了而已。从始至终,b的副本的头指针都没有变。
后来我在单链表类加了个返回头指针的引用的函数,用(node*)型的b2来接收它,其实数据类型没对上,然而我的dev-c++并不报错,结果也很自然地什么用也没有。最后我改用了一个(node *&)类型的变量来接受,问题解决了。
总结
如果仅仅是想让程序正常跑起来,其实有这样两个解决方案:
方案一:
给合并链表的函数传参数时,改用传指针或引用,反正别传值,不在函数结束时发生析构,就不会有那么多幺蛾子了。
方案二:
合并两个链表也不一定要把另一个链表的结点原封不动地拆过来,比如可以给a链表新建结点,这样a链表根本就不会和b链表发生连接。但是,如果你的函数采用传值在函数中建立副本,请小心析构。
代码:
//单链表 #include<iostream> #include<cstdlib> #include<ctime> using namespace std; struct node { int data; node* next; node() { next = NULL; } }; //-------------------------------------------------- class list { private: node* head, * rear; public: list() { head = rear = NULL; } void create(); //构建链表,从键盘输入数据 void create_2(); //构建链表,自动生成数据,有序 void print(); //输出链表到屏幕 ~list(); //释放内存 void sort(list b); //排序 node* get_head(); node*& get_phead(); }; //-------------------------------------------------- node*& list::get_phead() { return head; } //-------------------------------------------------- node* list::get_head() { return head; } //-------------------------------------------------- list::~list() { node* t; while (head) { cout << "~析构~" << endl; t = head; head = head->next; delete t; } cout << endl; } //-------------------------------------------------- void list::print() { node* t = head->next; while (t != NULL) { cout << t->data << " "; t = t->next; } cout << endl; } //-------------------------------------------------- void list::create() { head = new node; rear = head; node* t; int n(0), t_data(0); cin >> n; //数据的数量 for (int i(0); i < n; ++i) { t = new node; //建立新结点 cin >> t_data; t->data = t_data; rear->next = t; rear = rear->next; } } //-------------------------------------------------- void list::create_2() { srand(time(0)); int data(0); head = new node; rear = head; node* t; int n(0), t_data(0); cin >> n; //数据的数量 for (int i(0); i < n; ++i) { t = new node; //建立新结点 data += rand() % 6; t_data = data; t->data = t_data; rear->next = t; rear = rear->next; } } //-------------------------------------------------- void list::sort(list b) { node* a1(head), * b1(b.get_head()); node* t(NULL); b1 = b1->next; while (b1 != NULL && a1->next != NULL) { if (b1->data <= a1->next->data) { t = b1->next; b1->next = a1->next; a1->next = b1; b1 = t; } else { a1 = a1->next; } print(); } if (b1 != NULL) { a1->next = b1; } cout << "last b:" << endl; b.print(); node*& b2 = b.get_phead(); b2->next = NULL; b2 = NULL; //不能去掉 } int main() { list a, b; cout << "输入两个数字,代表两个序列的长度:"; a.create_2(); b.create_2(); a.print(); b.print(); a.sort(b); cout << "合并后a:" << endl; a.print(); return 0; }