关于顺序表交并集的一些思考——深浅拷贝问题

关于顺序表交并集的一些思考——深浅拷贝问题

该题所用到的顺序表

下面展示 顺序表的底层实现

#pragma once
#include
using namespace std;

//1、试用顺序表表示集合,并确定合适的约定,在此基础上编写算法以实现集合的交、并、差等运算,并分析各算法的时间性能。(3*)
enum Error_code {
      Success, Range_error, Overflow, Underflow };
          
template<class T>
class Sequence_List {
     
public:
	Sequence_List(int maxlen = 10);                                               //构造函数初始化
	Sequence_List(const Sequence_List<T> &SL);                                    //拷贝构造
	void copy(const Sequence_List<T>& L);                                         //复制函数(不改变容量,只将L的元素装入此类)
	bool empty();                                                                 //判断表是否为空
	bool full();                                                                  //判断表是否为满
	unsigned int size() const;                                                    //求顺序表中的元素数
	unsigned int capacity() const;                                                //求顺序表中的容量
	int find(const T &x) const;                                                   //按值查找元素并返回元素位置
	Error_code get_element(const int i, T &x) const;                              //获取i号位置的元素,并传入x来接受(传入的x被i号赋值,要被改变,故用&)
	Error_code insert(const int i, const T x);                                    //将元素x插入到i位置
	Error_code delete_element(const int i);                                       //删除i号位置元素
	//Sequence_List Union(Sequence_List &A,Sequence_List &B);           //求两表的并集
	//Sequence_List Intersection(Sequence_List &A, Sequence_List &B);   //求两表的交集
	//Sequence_List Difference_Set(Sequence_List &A, Sequence_List &B); //求两表的差集
	void Union(Sequence_List<T> &A, Sequence_List<T> &B);                         //求两表的并集
	void Intersection(Sequence_List<T> &A, Sequence_List<T> &B);                  //求两表的交集
	void Difference_Set(Sequence_List<T> &A, Sequence_List<T> &B);                //求两表的差集
	void removeDuplicate();                                                       //去重
	void print();                                                                 //打印表
	~Sequence_List();                                                             //析构函数
private:
	T *data;
	unsigned int count;                                      //元素数 
	int m_capacity;                                          //数组的容量
};

template<class T>
inline Sequence_List<T>::Sequence_List(int maxlen)// :m_capacity(maxlen)  //初始化列表给m_capacity赋初值
{
     
	//若重名
	//this->m_capacity = maxlen;
	data = new T[maxlen];
	m_capacity = maxlen;
	count = 0;
}

template<class T>
inline Sequence_List<T>::Sequence_List(const Sequence_List<T>& SL)
{
     
	m_capacity = SL.m_capacity;
	count = SL.count;
	data = new T[m_capacity];
	for (int i = 0; i < count; i++)
		data[i] = SL.data[i];
}

//仅仅是拷贝其他的值,没改变空间与容量
template<class T>
inline void Sequence_List<T>::copy(const Sequence_List<T>& SL)
{
     
	for (int i = 0; i < SL.size(); i++)
		data[i] = SL.data[i];
	count = SL.size();
}



template<class T>
inline bool Sequence_List<T>::empty()
{
     
	if (count == 0) return true;
	return false;
}

template<class T>
inline bool Sequence_List<T>::full()
{
     
	if (count == m_capacity) return true;
	return false;
}

template<class T>
inline unsigned int Sequence_List<T>::size() const
{
     
	return count;
}

template<class T>
inline unsigned int Sequence_List<T>::capacity() const
{
     
	return m_capacity;
}

template<class T>
inline int Sequence_List<T>::find(const T & x) const
{
     
	for (int i = 0; i < count; i++) 
		if (data[i] == x) return (i + 1);
	return -1;
}

template<class T>
inline Error_code Sequence_List<T>::get_element(const int i, T & x) const
{
     
	if (i<1 || i>count)return Range_error;
	x = data[i - 1];
	return Success;
}

template<class T>
inline Error_code Sequence_List<T>::insert(const int i, const T x)
{
     
	if (count == m_capacity) return Overflow;
	if (i<1 || i>count + 1) return Range_error;
	//j指向待移走线性表中元素序号
	for (int j = count; j >= i; j--) data[j] = data[j - 1];
	data[i - 1] = x;
	count++;
	return Success;
}

template<class T>
inline Error_code Sequence_List<T>::delete_element(const int i)
{
     
	if (count == 0) return Underflow;
	if (i<1 || i>count) return Range_error;
	for (int j = i + 1; j <= count; j++)
		data[j - 2] = data[j - 1];
	count--;
	return Success;
}

template<class T>
//inline Sequence_List Sequence_List::Union(Sequence_List &A, Sequence_List &B)
inline void Sequence_List<T>::Union(Sequence_List<T> &A, Sequence_List<T> &B)
{
     
	if (data != NULL)
	{
     
		//先把原来释放干净
		delete[]data;
		data = NULL;
	}
	//深拷贝
	m_capacity = A.m_capacity+B.m_capacity;
	data = new T[m_capacity];
	for (int i = 0; i < count; i++)
		data[i] = A.data[i];
	count = A.size();
	for (int i = 0; i < B.size(); i++) {
     
		if (this->find(B.data[i]) == -1)
			this->insert(this->size() + 1, B.data[i]);
	}
}

//p.s.形参列表传入类,相当于是值传递(不过这个类中的数组是地址传递),相当于再建了一个形参的类(调用了拷贝和析构)
//因此一般用引用传递来减少时间和空间
//即传递对象值会引起拷贝构造和析构,增加空间开销
template<class T>
//inline Sequence_List Sequence_List::Intersection(Sequence_List &A, Sequence_List &B)
inline void Sequence_List<T>::Intersection(Sequence_List<T> &A, Sequence_List<T> &B)
{
     
	//注意:成员函数的参数为类对象时,可以通过类对象直接访问其私有成员
	//因为函数Sequence_List::Intersection(Sequence_List A, Sequence_List B)是Sequence_List类的成员函数,函数处于类的内部,
	//因此对于相同类的对象,是可以通过对象名.私有成员来调用的。若是在类的外部,如在main()中使用对象名.私有成员是一定会报错的。
	//Sequence_List C;
	/*for (int i = 0; i < A.count; i++) {
		for (int j = 0; j < B.count; j++) {
			if (A.data[i] == B.data[j]) {
				C.insert(1, A.data[i]);
			}
		}
	}*/
	/*for (int i = 0; i < A.count; i++) {
		if (B.find(A.data[i]) != -1)
			C.insert(C.count+1, A.data[i]);
	}
	return C;*/
	for (int i = 0; i < A.size(); i++) {
     
		if (B.find(A.data[i]) != -1)
			this->insert(this->count + 1, A.data[i]);
	}
}

template<class T>
//inline Sequence_List Sequence_List::Difference_Set(Sequence_List &A, Sequence_List &B)
inline void Sequence_List<T>::Difference_Set(Sequence_List<T> &A, Sequence_List<T> &B)
{
     
	this->copy(A);
	for (int i = 0; i < this->count; i++)
		this->delete_element(this->find(this->data[i]));
}

template<class T>
inline void Sequence_List<T>::removeDuplicate()
{
     
	/*for (int i = 0; i < count; i++) {
		while (data[i] == data[i + 1])
			delete_element(i+1);
	}*/
	/*你的数组在内存中占据着一片连续空间
	看似删除i处的操作,实际上是i后面所有的数往前挪了一下
	同时,由于你做了count的限制,真正挪动的区间只从i+1~count
	但是你的数组在定义时的大小是maxlen,一定大于count,也就是说,count~maxlen-1这一段区间,实际上是合法区间,只不过对于此时大小为count的数组是没意义的
	删除操作本质就是挪动区间的操作
	那么抛开具体的例子,假设我往前挪动一个单位的[i,j]闭区间
	那么[i,j]上的数据到了[i-1, j-1]
	[i,j]上的数据到了[i-1, j-1],换句话说,i-1处的数据被覆盖了
	那么请问,此时的[j]处的数据是什么?
	是不是从未变过
	所以上面的区间[i,j]中的j实际上就是9所在的位置
	也就是说,当你多次挪动区间,9被多次拷贝且不未被覆盖
	因为没有数在他后面
	所以当你完成去重操作,你的数组就会是12345678999999……(一直到18个位置)
	*/
	//错误算法:原因分析:while特点,一直判断,直到条件不满足才会退出。
	//当i为7,count为10时,此时1234567889 data[i]=8,data[i+1]=8 delete第八号,覆盖data[7]
	//当i为7,count为9时,此时123456789(9) data[i]=8,data[i+1]=9 i++
	//当i为8,count为9,此时123456789(9) data[i]=9,data[i+1]=? 答案还是9,不过这个9不在delete管控范围内,因此删(覆盖)不掉
	//从而导致一直while循环成立,但delete每次Range_errror跳出(陷入死循环)
	//启示:不要做越界(其实也不算叫越界)
	for (int i = 1; i < count; i++) {
     
		while (data[i - 1] == data[i])
			delete_element(i);
	}
}

template<class T>
inline void Sequence_List<T>::print()
{
     
	for (int i = 0; i < count; i++)
		cout << data[i] << " ";
	cout<<endl;
}

template<class T>
inline Sequence_List<T>::~Sequence_List()
{
     
	if (data!=NULL)
		delete[] data;
}

该题的功能实现

其中 交集

template<class T>
inline void Sequence_List<T>::Intersection(Sequence_List<T> &A, Sequence_List<T> &B)
{
     
	/
	for (int i = 0; i < A.size(); i++) {
     
		if (B.find(A.data[i]) != -1)
			this->insert(this->count + 1, A.data[i]);
	}
}

其中 并集。(扩充了容量为A,B之和)

template<class T>
inline void Sequence_List<T>::Union(Sequence_List<T> &A, Sequence_List<T> &B){
     
	if (data != NULL){
     
		//先把原来释放干净
		delete[]data;
		data = NULL;
	}
	//深拷贝
	m_capacity = A.m_capacity+B.m_capacity;
	data = new T[m_capacity];
	for (int i = 0; i < count; i++)
		data[i] = A.data[i];
	count = A.size();
	for (int i = 0; i < B.size(); i++) {
     
		if (this->find(B.data[i]) == -1)
			this->insert(this->size() + 1, B.data[i]);
	}
}

其中 差集

template<class T>
inline void Sequence_List<T>::Difference_Set(Sequence_List<T> &A, Sequence_List<T> &B)
{
     
	this->copy(A);
	for (int i = 0; i < this->count; i++)
		this->delete_element(this->find(this->data[i]));
}

测试代码

#include"Linked_List.hpp"
int main() {
     
	//假设链表A、B分别表示两个集合,设计算法以求解C = A∪B,并分析算法的时间复杂度。 (3*)
	//假设递增有序链表A、B分别表示一个集合,设计算法以求解C= A∩B,并分析算法的时间复杂度。
	Linked_List<int> C;
	for (int i = 0; i < 5; i++)
		C.insert(C.size() + 1, i);
	C.print();
	Linked_List<int> D;
	for (int i = 3; i < 8; i++)
		D.insert(D.size() + 1, i);
	D.print();
	Linked_List<int> E;
	E.Union(C, D);
	E.print();
	Linked_List<int> F;
	F.Intersection(C, D);
	F.print();
	return 0;
}

输出结果为
0 1 2 3 4
3 4 5 6 7
0 1 2 3 4 7 6 5
3 4

该题的错误示例

以上是我改正之后的代码
但在此之前,我的这三个分别是这样的

template<class T>
inline Sequence_List<T> Sequence_List<T>::Union(Sequence_List<T> &A, Sequence_List<T> &B)
{
     
	Sequence_List<T> C(A);
	for (int i = 0; i < B.count; i++) {
     
		if (C.find(B.data[i]) == -1)
			C.insert(C.count + 1, B.data[i]);
	}
	return C;
	/*this->copy(A);
	for (int i = 0; i < B.size(); i++) {
		if (this->find(B.data[i]) == -1)
			this->insert(this->size() + 1, B.data[i]);
	}*/
}

template<class T>
inline Sequence_List<T> Sequence_List<T>::Intersection(Sequence_List<T> &A, Sequence_List<T> &B)
//inline void Sequence_List::Intersection(Sequence_List &A, Sequence_List &B)
{
     
	Sequence_List<T> C;
	for (int i = 0; i < A.count; i++) {
     
		if (B.find(A.data[i]) != -1)
			C.insert(C.count+1, A.data[i]);
	}
	return C;
	/*for (int i = 0; i < A.size(); i++) {
		if (B.find(A.data[i]) != -1)
			this->insert(this->count + 1, A.data[i]);
	}*/
}

template<class T>
inline Sequence_List<T> Sequence_List<T>::Difference_Set(Sequence_List<T> &A, Sequence_List<T> &B)
//inline void Sequence_List::Difference_Set(Sequence_List &A, Sequence_List &B)
{
     
	Sequence_List<T> D(A);
	Sequence_List<T> C = C.Intersection(A, B);
	for (int i = 0; i < C.count;i++)
		D.delete_element(D.find(C.data[i]));
	return D;
	/*this->copy(A);
	for (int i = 0; i < this->count; i++)
		this->delete_element(this->find(this->data[i]));*/

}

即类中的这三个成员函数并不是对这个类对象本身进行操作的
而是又创建了另一个对象,对其进行操作后再返回
因此,测试代码需要进行相应的改变,如下:

#include"Sequence_List.hpp"
int main() {
     
	Sequence_List<int> A(12);
	A.insert(A.size() + 1, 1);
	A.insert(A.size() + 1, 2);
	A.insert(A.size() + 1, 4);
	A.print();
	Sequence_List<int> B(6);
	B.insert(B.size() + 1, 3);
	B.insert(B.size() + 1, 2);
	B.insert(B.size() + 1, 4);
	B.print();
	Sequence_List<int> C;
	C=C.Intersection(A, B);
	C.print();
	Sequence_List<int> D ;
	D= D.Union(A, B);
	D.print();
	Sequence_List<int> E ;
	E = E.Difference_Set(A, B);
	E.print();
	Sequence_List<int> F ;
	F = F.Difference_Set(B, A);
	F.print();
	return 0;
}

对错误的分析

看着仿佛没有什么问题,但系统,显示已触发了一个断点,这是为什么呢?
其实,因为此顺序表开辟的是动态数组,在堆区开辟了空间,自然此表的析构函数会去释放堆区空间

template<class T>
inline Sequence_List<T>::~Sequence_List()
{
     
	if (data!=NULL)
		delete[] data;
}

这样的析构函数,我们暂且称为深析构,这时候往往要注意浅拷贝带来的堆区内存重复释放问题。即系统中提供的默认拷贝构造函数往往只会提供浅拷贝的操作,即简单的值拷贝

template<class T>
inline Sequence_List<T>::Sequence_List(const Sequence_List<T>& SL)
{
     
	m_capacity = SL.m_capacity;
	count = SL.count;
	data = SL.data;
}

关键问题就出在

data=SL.data;

此时数组中的data是原先在堆区new出的一块内存,data为指针。
若仅仅做如上操作,相当于把此对象this->data指针指向了要拷贝的SL.data指针指向的内存,即两个指针指向同一块内存,这样我对此对象进行的操作就会对SL产生影响,两者共同对这块区域有操作权,但这不是我们希望的,我们不希望对SL产生影响,而且程序结束时,此对象this先释放(执行析构函数),把SL在堆区new出来的内存已经还给了系统,然后SL再释放(析构)此时已经没有内存可以还了,出现堆区内存重复释放问题。因此,我们在这里要做一个深拷贝的操作。

template<class T>
inline Sequence_List<T>::Sequence_List(const Sequence_List<T>& SL)
{
     
	m_capacity = SL.m_capacity;
	count = SL.count;
	data = new T[m_capacity];
	for (int i = 0; i < count; i++)
		data[i] = SL.data[i];
}

重新给this对象开辟一片与SL相同长度的空间,并把其中的值拷贝到this对象的新开辟的data空间中。

但明明已经做了深拷贝,为什么还会出现已经释放了的内存重复删除的情况呢?我们对如下代码进行分析

//功能实现
template<class T>
inline Sequence_List<T> Sequence_List<T>::Union(Sequence_List<T> &A, Sequence_List<T> &B)
{
     
	Sequence_List<T> C(A);
	for (int i = 0; i < B.count; i++) {
     
		if (C.find(B.data[i]) == -1)
			C.insert(C.count + 1, B.data[i]);
	}
	return C;
}
//测试代码
   Sequence_List<int> D ;
	D= D.Union(A, B);
	D.print();

分析这个并集功能实现,首先先将A拷贝给了C(深拷贝),再将遍历B,将B中C没找到的元素插入C末端。最后返回了C(其实是C的临时拷贝)。

下面我们来具体的分析一下这个函数的构造与析构情况,首先,因为A,B传入的不是值,是引用,故在形参不调用析构,然后在函数体中创建了C,调用了一次拷贝构造,这个局部变量C,在return之前被析构掉了,但因为此时return的是C的临时拷贝的类(即在C被析构之前还调用了一次拷贝构造创建了一个临时的类),然后这个临时类在将其一些属性值返回后就立即释放了。

问题在这个时候浮现了

D= D.Union(A, B);

对这一过程,临时类被返回给了D.Union(A, B);然后被赋值给了D,赋值之后立马释放了(析构),但注意对两个类的=,按道理说是无法进行了,但系统会给你提供一个默认的=运算符重载,不过这个重载的=运算符,仅做了浅拷贝,故D只得到了这个临时类的count,capacity和data的地址,注意仅仅是将D的data指针指向了这个临时类的data空间(共用一块空间),但由于这个临时类做完=浅拷贝操作后就立马析构掉了(将其data中new出来堆区的空间还给了系统),故此时D中data指针指向的地址是一片已经还给系统的非法空间,后期对D进行打印的时候,访问了非法空间。

改正方案

那如何修改呢?

方案1

直接在构造交并差类的时候,返回拷贝C.Intersection(A, B), D.Union(A, B); E.Difference_Set(A, B);的临时变量,在构造这个类的时候系统调用的不是默认的重载=运算符,而是你写的拷贝构造函数。

	Sequence_List<int> C=C.Intersection(A, B);
	//C=C.Intersection(A, B);
	C.print();
	Sequence_List<int> D = D.Union(A, B);
	//D= D.Union(A, B);
	D.print();
	Sequence_List<int> E = E.Difference_Set(A, B);
	//E = E.Difference_Set(A, B);
	E.print();
	Sequence_List<int> F = F.Difference_Set(B, A);
	//F = F.Difference_Set(B, A);
	F.print();
	return 0;
}

p.s.对调用构造函数的方式的回顾

void test02()
{
     
	//1、括号法  (建议使用)
	Person2 p11;    //默认构造函数的调用,不要加()
	Person2 p12(10);//有参构造函数调用
	Person2 p13(p12);//拷贝构造函数的调用

	//注意事项
	//调用默认构造函数时,不要加()
	//因为下面这行代码,编译器会认为是一个函数的声明(在一个函数体内部允许写另一个函数声明)
	//Person2 p4();
	//void func();类比

	cout << "p12的年龄为:" << p12.age << endl;
	cout << "p13的年龄为:" << p13.age << endl;//拷贝年龄
	
										
	//2、显示法
	Person2 p21;               //默认构造函数的调用,不要加()
	Person2 p22 = Person2(10); //有参构造函数调用(相当于给匿名对象取名)
	Person2 p23 = Person2(p22);//拷贝构造函数的调用

	Person2(10);//匿名对象    特点:当前行执行结束后,系统会立即回收掉匿名对象
	//即下面那行aaaaaaa的代码在匿名对象的析构函数之后执行
	//即匿名对象在下一行代码执行之前已被销毁
	cout << "aaaaaaa" << endl;

	//注意事项2
	//不要利用拷贝构造函数  初始化匿名对象
	//编译器会认为 Person (p3) == Person p3;(对象的声明)
	//Person2(p23); 错误 这样相当于一个无参构造而上面已经有一个p23了


	//3、隐式转换法
	Person2 p32 = 10;//相当于  写了  Person p32 = Person2(10);  有参构造
	Person2 p33 = p32;//拷贝构造
}

方案2

在临时变量还没被析构时即输出

Sequence_List<int> C(A);
	C.Intersection(A, B).print();
	Sequence_List<int> D;
	D.Union(A, B).print();
	Sequence_List<int> E;
	E.Difference_Set(A, B).print();
	Sequence_List<int> F;
	F.Difference_Set(B, A).print();

方案3(重载=运算符)(跪求大佬解释为啥vc可以编译,vs不行)

template<class T>
inline Sequence_List<T>& Sequence_List<T>::operator=(Sequence_List<T> & SL)
{
     
	//编译器是提供浅拷贝
	//应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
	if (data != NULL)
	{
     
		//先把原来释放干净
		delete data;
		data = NULL;
	}
	//深拷贝
	m_capacity = SL.m_capacity;
	count = SL.count;
	data = new T[m_capacity];
	for (int i = 0; i < count; i++)
		data[i] = SL.data[i];
	//返回对象本身
	return *this;
}

p.s.我不知道为什么vs识别不出=在这里插入图片描述
但vc可以正确运行
关于顺序表交并集的一些思考——深浅拷贝问题_第1张图片
跪求大佬解释为啥vc可以编译,vs不行!

总结——拷贝构造函数调用时机

C++中拷贝构造函数调用的时机通常有三种情况:

1 使用一个已经创建完毕的对象来初始化一个新对象
Person p2(p1);
2 值传递的方式给函数参数传值
void doWork(Person p)//值传递{
     
}

void test2(){
     
	Person p0;//调用默认构造
	doWork(p0);//调用这个函数的时候是用值传递的方法
	          //值传递的本质是会拷贝一个临时的副本出来(调用一个拷贝构造函数,在创建一个新的东西)
	          //实参在传给形参的时候会调用一个拷贝构造函数
	          //形参p会按照实参p拷出一个新的数据
	          //所以,现在操纵形参不会影响实参
	    
}

实参p0传递给了形参p,在形参列表中,调用拷贝构造函数构造了一个局部变量p,在函数中对p操作一般不会改变p0(除非p0的成员变量中有指针)
这个p在函数执行完后析构

3 以值方式返回局部对象
以值方式返回局部对象
//在一个函数里定义的对象叫局部对象
//局部对象有一个特点在函数执行完之后就会被释放掉
Person doWork2(){
     
	Person p1;//创建局部对象  //会调用默认构造函数
	cout << (int*)&p1 << endl;
	return p1;//但返回的不是p1本身
	//是用值方式来返回,这里面会根据p1来创建(拷贝)一个新的对象,然后返回给test
}
//在doWork2执行完后原p1被释放,产生析构函数,在这个过程之前,return产生了一个拷贝函数
void test(){
     
	Person p = doWork2();
	cout << (int*)&p << endl;//两地址不一样说明返回的p1(外侧用p接收的p1)与原p1不是同一个对象
}

运行结果:
Person的默认构造函数的调用 (创建p1)
00EFF6F4 (p1的地址)
Person的拷贝构造函数调用 (临时类对p1的拷贝)
Person 析构函数的调用 (局部对象p1的析构)
00EFF7EC (临时对象把地址传给了p,打印p的地址)
Person 析构函数的调用 (p的析构)

但是很奇怪的是,为什么临时类没有析构函数,p没有拷贝构造函数?
其实像下面这样将Person p = doWork2()分开写就有了。
(为了更好判断,在调用前写上this)

class Person9{
     
public:
	Person9(){
     
		cout<<this << "Person3的默认构造函数的调用" << endl;
	}
	Person9(int age){
     
		cout<<this<< "Person3的有参构造函数调用" << endl;
		m_Age = age;
	}
	Person9(const Person9 & p){
     
		cout<<this<< "Person3的拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
	}
	~Person9(){
     
		cout << this << "Person3  析构函数的调用" << endl;
	}
	int m_Age;
};
Person9 doWork2()
{
     
	Person9 p1;//创建局部对象  //会调用默认构造函数
	cout << &p1 << endl;//p1为对象指针
	return p1;//但返回的不是p1本身
	//是用值方式来返回,这里面会根据p1来创建(拷贝)一个新的对象,然后返回给test9
}
//在doWork2执行完后原p1被释放,产生析构函数,在这个过程之前,return产生了一个拷贝函数
void test9()
{
     
	Person9 p;
	p=doWork2();
	cout <<(int*) &p << endl;//强行把对象指针转化为整型指针(int)&p才是转化为十进制
	//两地址不一样说明返回的p1(外侧用p接收的p1)与原p1不是同一个对象
}

输出结果:
0056F974 Person3的默认构造函数的调用 (test9中p的构造)
0056F86C Person3的默认构造函数的调用 (doWork2()中p1的构造)
0056F86C
0056F8A8 Person3的拷贝构造函数调用
(根据p1来创建(拷贝)一个新临时的对象,然后返回给test9)
0056F86C Person3 析构函数的调用 (doWork2()中p1的析构)
0056F8A8 Person3 析构函数的调用
(在完成对p的赋值doWork2()中临时对象的析构)
0056F974
0056F974 Person3 析构函数的调用 (test9中p的析构)

分析:为什么分开写就会有临时类析构函数,p构造函数,合在一起写没有呢?(相当于显示法调用拷贝构造)对于Person2(p22)的匿名对象拷贝析构省略。(可能说的不太严谨)

//2、显示法
	Person2 p21;               //默认构造函数的调用,不要加()
	Person2 p22 = Person2(10); //有参构造函数调用(相当于给匿名对象取名)
	Person2 p23 = Person2(p22);//拷贝构造函数的调用

	Person2(10);//匿名对象    特点:当前行执行结束后,系统会立即回收掉匿名对象
	//即下面那行aaaaaaa的代码在匿名对象的析构函数之后执行
	//即匿名对象在下一行代码执行之前已被销毁
	cout << "aaaaaaa" << endl;

你可能感兴趣的:(数据结构,链表)