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


编辑

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

    


全部评论

相关推荐

11-27 12:36
已编辑
门头沟学院 前端工程师
Apries:这个阶段来说,很厉害很厉害了,不过写的简历确实不是很行,优势删掉吧,其他的还行
点赞 评论 收藏
分享
offer多多的六边形战士很无语:看了你的博客,感觉挺不错的,可以把你的访问量和粉丝数在简历里提一下,闪光点(仅个人意见)
点赞 评论 收藏
分享
评论
1
1
分享
牛客网
牛客企业服务