写点什么

合并两个有序单链表,对象析构这一着我实在没想到。

作者:阿锋
  • 2022 年 8 月 16 日
    湖南
  • 本文字数:2444 字

    阅读完需:约 8 分钟

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

复制代码


发布于: 刚刚阅读数: 2
用户头像

阿锋

关注

还未添加个人签名 2022.08.09 加入

还未添加个人简介

评论

发布
暂无评论
合并两个有序单链表,对象析构这一着我实在没想到。_8月月更_阿锋_InfoQ写作社区