【C++---14】类与对象(中):对象数组,对象成员,深拷贝和浅拷贝'

目录

对象数组:

对象成员:

小题题:

深拷贝和浅拷贝:

浅拷贝实践:

深拷贝实践:

比较:


对象数组:

class Coordinate
{
public:
	int m_iX;
	int m_iY;
};

int main(void)
{
	Coordinate coord[3];//栈上实例化数组
	coord[1].m_iX = 10;//访问数据成员
	Coordinate *p = new Coordinate[3];//堆上实例化数组
	p[0].m_iY = 20;//
	p->m_iY = 20;//指针访问赋值
	delete[]p;
	p = NULL;
	return 0;
}

 

代码演示:


/*
对象数组:
要求:
     1.定义Coordinate类
	 2.数据成员:m_iX和m_iY
	 3.分别从栈和堆中实例化长度为3的对象数组
	 4.给数组元素分别赋值
	 5.遍历两个数组
*/
class Coordinate
{
public:
	Coordinate();
	~Coordinate();
public:
	int m_iX;
	int m_iY;
};

Coordinate::Coordinate()
{
	cout << "Coordinate" << endl;

}
Coordinate::~Coordinate()
{
	cout << "~Coordinate" << endl;

}


int main(void)
{
	Coordinate coor[3];//栈上实例化数组
	coor[0].m_iX = 1;
	coor[0].m_iY = 3;//访问数据成员
	Coordinate *p = new Coordinate[3];//堆上实例化数组
	p[0].m_iX= 2;//
	p->m_iY = 4;//指针访问赋值,第一个元素

	p++;
	p[0].m_iX = 6;//第二个元素
	p->m_iY = 8;

	p[1].m_iX = 10;//第三个元素赋值
	p++;
	p->m_iY = 12;


	for (int i = 0; i < 3; i++)
	{
		cout << "coor_x"<<coor[i].m_iX << endl;
		cout << "coor_y"<<coor[i].m_iY << endl;
	}
	/*
	//假设没有进行过p++
	for (int j = 0; j < 3; j++)
	{
		cout <<"p_x"<< p[j].m_iX << endl;
		cout << "p_y"<<p[j].m_iY << endl;
	}
	*/
	//事实上:
	for (int j = 0; j < 3; j++)
	{
		cout << "p_x" << p->m_iX << endl;
		cout << "p_y" << p->m_iY << endl;
		p--;
	}
	p++;//
	delete[]p;
	p = NULL;
	return 0;
}

实例化对象数组时,每一个对象的构造函数都会被执行。

实例化对象数组时,内存既可以从堆上分配,也可以从栈上分配。

销毁对象数组时,每一个对象的析构函数都会被执行。

堆中实例化的数组需要手动销毁释放内存,在栈中实例化的数组,系统自动回收内存.

对象成员:

对象成员:对象中还可能包含着其他对象

代码演示:

demo.cpp

#include<iostream>
#include<stdlib.h>
#include<string>
#include"Line.h"
using namespace std;
/*对象成员
要求:
     定义两个类:
	坐标类:Coordinate
	数据成员:m_iX和m_iY
	成员函数:构造函数,析构函数,数据封装函数
	线段类:Line
	数据成员:点A m_coorA,点B m_coorB
	成员函数:构造函数,析构函数,数据封装函数,信息打印函数*/

int main(void)
{
	Line *p = new Line();
	delete p;
	p = NULL;

	system("pause");
	return 0;
}
Line.cpp

#include"Line.h"
#include<iostream>
using namespace std;

Line::Line()
{
	cout << "Line" << endl;
}
Line::~Line()
{
	cout << "~Line" << endl;
}
void Line::setA(int x, int y)
{
	m_coorA.setX(x);
	m_coorA.setY(y);
}
void Line::setB(int x, int y)
{
	m_coorB.setX(x);
	m_coorB.setY(y);
}

void Line::printInfo()
{
	cout << "(" << m_coorA.getX() << m_coorA.getY() << ")" << endl;
	cout << "(" << m_coorB.getX() << m_coorB.getY() << ")" << endl;
}

Line.h
#include"Coordinate.h"

class Line
{
public:
	Line();
	~Line();
	void setA(int x,int y);
	void setB(int x,int y);
	void printInfo();
private:
	Coordinate m_coorA;
	Coordinate m_coorB;
};
Coordinate.cpp

#include"Coordinate.h"
#include<iostream>
using namespace std;

Coordinate::Coordinate()
{
	cout << "Coordinate()" << endl;
}
Coordinate::~Coordinate()
{
	cout << "~Coordinate()" << endl;
}
void Coordinate::setX(int x)
{
	m_iX = x;
}
int Coordinate::getX()
{
	return m_iX;
}
void Coordinate::setY(int y)
{
	m_iY = y;
}
int Coordinate::getY()
{
	return m_iY;
}
Coordinate.h

class Coordinate
{
public:
	Coordinate();
	~Coordinate();
	void setX(int x);
	int getX();
	void setY(int y);
	int getY();
private:
	int m_iX;
	int m_iY;
};

实例化对象A时,如果对象A有对象成员B,那么先执行对象B的构造函数,再执行A的构造函数。

如果对象A中有对象成员B,那么销毁对象A时,先执行对象A的析构函数,再执行B的析构函数。

如果对象A中有对象成员B,对象B没有默认构造函数,那么对象A必须在初始化列表中初始化对象B。

小题题:

定义具有2个对象的Coordinate数组,遍历对象数组,打印对象信息

结果如图所示

#include <iostream>
using namespace std;
class Coordinate
{
    
public:
	Coordinate()
	{
	}
	// 打印坐标的函数
	void printInfo()  
	{
	   cout<<"("<<m_iX<<","<<m_iY<<")"<<endl; 
	}
public:
	int m_iX;
	int m_iY;
};
int main(void)
{
	//定义对象数组
    Coordinate coorArr[2] ;
    coorArr[0].m_iX=1;
    coorArr[0].m_iY=2;
    coorArr[1].m_iX=3;
    coorArr[1].m_iY=4;
    




	//遍历数组,打印对象信息
	for(int i = 0; i < 2; i++)
	{
		coorArr[i].printInfo();
	}	
	return 0;
}

深拷贝和浅拷贝:

浅拷贝:拷贝后指针指向相同的内存。

深拷贝:申请新的内存,逐位存放内容;

浅拷贝后:对象销毁存在问题,同一块内存释放两次;

 

为了达到两个对象指针指向不同内存,拷贝时是将内存中的数据拷贝过来;

深拷贝代码如下:

在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,并用for循环将每一个成员都拷贝即进行深拷贝,这样就避免了内存泄漏发生。

 

 

浅拷贝实践:

 

  1. 定义一个Array类;
  2. 数据成员包括数据封装函数,构造函数,拷贝构造函数,和析造函数;
  3. 通过此示例体会浅拷贝原理
  • Array.h
class Array
{
public:
	Array();//构造函数
	Array(const Array&arr);//拷贝构造函数
	~Array();//析构函数
		void setCount(int count);
	int getCount();

	private:
		int m_iCount;
};
  • Array.cpp
  • /*
    1.定义一个Array类,
          数据成员包括数据封装函数,构造函数,拷贝构造函数,和析造函数
    	  2.通过此示例体会浅拷贝原理
    	  3.增加数据成员m_pArr,增加m_pArr地址查看函数,
    	  同时改造构造函数,拷贝构造函数和析造函数,体会深拷贝的原理和必要性
    
    */
    
    #include"Array.h"
    #include<iostream>
    using namespace std;
    
    Array::Array()//构造函数
    {
    	cout << "Array" << endl;
    }
    Array::Array(const Array& arr)//拷贝函数
    {
    	m_iCount = arr.m_iCount;//拷贝
    	cout << "Array&" << endl;//加上&与前面的Array相区别
    }
    Array::~Array()//析构函数
    {
    	cout << "~Array" << endl;
    }
    void Array::setCount(int count)//数据的封装
    {
    	m_iCount = count;
    }
    int Array::getCount()
    {
    	return m_iCount;
    }
    int main(void)
    {
    	Array arr1;//实例化对象
    	arr1.setCount(10);
    
    	Array arr2(arr1);//说明浅拷贝,
    	cout << "arr2.m_iCount  " << arr2.getCount() << endl;
    
    	system("pause");
    	return 0;
    }

    Arr1和Arr2都指向同一内存,但是析构函数中的delete []m_pArr只有一个,所以会释放同一地址的内存两遍,此时程序就会卡在析构函数。

    浅拷贝直接赋值

深拷贝实践:

  1. 增加数据成员m_pArr,增加m_pArr地址查看函数,
  2. 同时改造构造函数,拷贝构造函数和析造函数,体会深拷贝的原理和必要;

Array.h

class Array
{
public:
	Array(int count);//构造函数
	Array(const Array&arr);//拷贝构造函数
	~Array();//析构函数
	void setCount(int count);
	 int getCount();
	 void printAddr();
	 void printArr();
	private:
		int m_iCount;
		int *m_pArr;
};

Array.cpp

/*
1.定义一个Array类,
      数据成员包括数据封装函数,构造函数,拷贝构造函数,和析造函数
	  2.通过此示例体会浅拷贝原理
	  3.增加数据成员m_pArr,增加m_pArr地址查看函数,
	  同时改造构造函数,拷贝构造函数和析造函数,体会深拷贝的原理和必要性

*/

#include"Array.h"
#include<iostream>
using namespace std;

Array::Array(int count)//构造函数
{
	m_iCount = count;
	m_pArr = new int[m_iCount];
	for (int i = 0; i < m_iCount; i++)
	{
		m_pArr[i] = i;
	}
	cout << "Array" << endl;
}
Array::Array(const Array& arr)//拷贝函数
{
	//m_pArr = arr.m_pArr;//浅拷贝,直接赋值

	m_iCount = arr.m_iCount;//拷贝.先复制
	m_pArr = new int[m_iCount];//分配内存
	
	for (int i = 0; i < m_iCount; i++)
	{
		m_pArr[i]= arr.m_pArr[i];//逐个拷贝,深拷贝
	}
	
	cout << "Array&" << endl;//加上&与前面的Array相区别
}
Array::~Array()//析构函数
{
	delete[]m_pArr;
	m_pArr = NULL;
	cout << "~Array" << endl;
}
void Array::setCount(int count)//数据的封装
{
	m_iCount = count;
}
int Array::getCount()
{
	return m_iCount;
}


void Array::printAddr()
{
	cout << "m_pArr的值:" << m_pArr << endl;
}
void Array::printArr()
{
	for (int i = 0; i < m_iCount; i++)
	{
		cout << m_pArr[i] << endl;
	}

}

int main(void)
{
	Array arr1(5);//实例化对象

	Array arr2(arr1);//说明浅拷贝,
	arr1.printArr();
	arr2.printArr();
	

	system("pause");
	return 0;
}

m_iCount = arr.m_iCount;

m_pArr = new int[m_iCount];

上面两个顺序不能换。

 

比较:

【浅拷贝】是增加了一个指针,指向原来已经存在的内存。而【深拷贝】是增加了一个指针,并新开辟了一块空间,让指针指向这块新开辟的空间。

【浅拷贝】在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误;

小点点:

  1. 拷贝对象时,不会执行构造函数,而是执行拷贝构造函数;
  2. 关于 深拷贝与浅拷贝需要知道的基本概念和知识:
  3. (1)什么时候用到拷贝函数?  
  4. a.一个对象以值传递的方式传入函数体;  
  5. b.一个对象以值传递的方式从函数返回;  
  6. c.一个对象需要通过另外一个对象进行初始化。
  7. 如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。
  8. 位拷贝又称浅拷贝 如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝
  9. 深拷贝时,需要在拷贝构造函数中,重新分配堆内存,并将堆内存地址上的值复制给新的堆内存;
  10.  
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计并实现一个动态整型数组类Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。 设计并实现一个动态整型数组类Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值