最初代码中把 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;
}
复制代码
评论