C++学习笔记5——封装篇(下)

深拷贝浅拷贝

在这里插入图片描述
定义一个类叫Array,定义一个数据成员 m_iCount ,在构造函数中给m_iCount赋初值为5,在拷贝构造函数中,传入的参数是arr,其数据类型也是Array,所以该拷贝构造函数中,也有数据成员m_iCount。拷贝构造函数中的语句是把arr中的m_iCount赋值给本身Array这个中的数据成员m_iCount。

使用时,若采用图中的方式进行实例化,那么会调用arr1的构造函数,(即把arr1中的m_iCount赋初值5)。下一句用arr1去初始化arr2,arr2实例化时调用arr2的构造函数。此时arr2中的拷贝构造函数Array(const = arr.m_iCount中的arr就是arr1,代码执行结果,就是arr1中的m_iCount的值赋给arr2中的m_iCount

修改一下:
在这里插入图片描述
增加了一个数据成员*m_pArr,操作与上例类似

以上两例都是对数据成员的值进行了简单拷贝,这样的拷贝模式成为 浅拷贝 。

在这里插入图片描述
浅拷贝,两个数据成员会指向同一块内存。当对arr1的数据成员赋值,再对arr2的数据成员赋值时,arr1的数据成员的值会被覆盖。销毁时,释放arr1的内存,再释放arr2的内存,这相当于同一块内存被释放两次,会出错。

在这里插入图片描述
上图才是我们想要达到的效果。
要达到以上效果,代码如下:
在这里插入图片描述
在构造函数中给m_iCount赋初值5,m_pArr指向一段内存,这段内存在堆中分配。
拷贝构造函数中,m_pArr不是直接由arr的m_pArr赋值,而是先分配一段内存。使用for循环,将arr的m_pArr中的每一个元素,都拷贝到当前的m_pArr所指向的相应的内存中

当进行对象拷贝时,不是简单的做值的拷贝,而是将堆中内存的数据也进行拷贝,这种拷贝模式就称为深拷贝

浅拷贝编码举例1:
Array.h

class Array
{
public:
	Array();
	Array(const Array &arr);
	~Array();
	void setCount(int count);
	int getCount();
	
private:
	int m_iCount;
};

Array.cpp

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

Array::Array() 
{
	cout << "Array()" << endl;
}
Array::Array(const Array &arr)
{
	m_iCount = arr.m_iCount;
	cout << "Array &" << endl;
}
Array::~Array()
{
	cout << "~Array()" << endl;
}
void Array::setCount(int count)
{
	m_iCount = count;
}
int Array::getCount()
{
	return m_iCount;
}

demo.cpp

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

int main()
{
	Array arr1; //实例化一个Array的对象
	arr1.setCount(5); //给arr1的m_iCount赋值为5

	Array arr2(arr1);  //通过arr1实例化Array的一个对象arr2,这样子会调用arr2的拷贝构造函数

	cout << "arr2.m_iCount:" << arr2.getCount() << endl;
	return 0;
}

浅拷贝编码举例2:
Array.h

class Array
{
public:
	Array(int count);
	Array(const Array &arr);
	~Array();
	void setCount(int count);
	int getCount();
	void printAddr();

private:
	int m_iCount;
	int *m_pArr;
};

Array.cpp

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

Array::Array(int count) 
{
	m_iCount = count;
	m_pArr = new int[m_iCount]; //给m_pArr申请内存
	cout << "Array()" << endl;
}
Array::Array(const Array &arr)
{ //浅拷贝
	m_pArr = arr.m_pArr;
	m_iCount = arr.m_iCount;
	cout << "Array &" << endl;
}
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;
}

demo.cpp

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

int main()
{
	Array arr1(5); //实例化一个Array的对象
	Array arr2(arr1);  //通过arr1实例化Array的一个对象arr2,这样子会调用arr2的拷贝构造函数

	arr1.printAddr();
	arr2.printAddr();
	return 0;
}

在这里插入图片描述
上图运行结果说明,两个对象使用的是同一块内存,会报错,因为销毁两个对象,同一块内存释放了两次。

深拷贝编码举例:
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

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

Array::Array(int count) 
{
	m_iCount = count;
	m_pArr = new int[m_iCount]; //给m_pArr申请内存
	for (int i = 0; i < m_iCount; i++)
	{
		m_pArr[i] = i;
	}
	cout << "Array()" << endl;
}
Array::Array(const Array &arr)
{ //深拷贝
	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()
{
	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;
	}
}

demo.cpp

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

int main()
{
	Array arr1(5); //实例化一个Array的对象
	Array arr2(arr1);  //通过arr1实例化Array的一个对象arr2,这样子会调用arr2的拷贝构造函数

	arr1.printArr();
	arr2.printArr();
	return 0;
}

对象指针

在这里插入图片描述
以上是在堆中实例化的对象的访问方法

用new 才会调用相关对象的构造函数
在这里插入图片描述

对象指针编码举例:
Coordinate.h

class Coordinate
{
public:
	Coordinate();
	~Coordinate();
	
public:
	int m_iX;
	int m_iY;
};

Coordinate.cpp

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

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

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

demo.cpp

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

int main()
{
	Coordinate *p1 = NULL;
	p1 = new Coordinate; //也可以写为 p1 = new Coordinate(); 因为定义了默认构造函数,有没有()都行
	Coordinate *p2 = new Coordinate();
	p1->m_iX = 10;
	p1->m_iY = 20;
	(*p2).m_iX = 30; //使指针变成了一个对象
	(*p2).m_iY = 40;
	cout << p1->m_iX + (*p2).m_iX << endl;
	cout << (*p1).m_iY + p2->m_iY << endl;
	delete p1;
	p1 = NULL;
	delete p2;
	p2 = NULL;

	Coordinate p3;
	Coordinate *p4 = &p3; //可以通过p4来操作p3的数据成员和函数
	p4->m_iX = 10;
	p4->m_iY = 20;
	cout << p3.m_iX << endl;
	cout << p3.m_iY << endl;
	return 0;
}

对象成员指针

编码举例:
Cooedinate.h

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

Coordinate.cpp

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

Coordinate::Coordinate(int x,int y)
{
	m_iX = x;
	m_iY = y;
	cout << "Coordinate()  " << m_iX << "," << m_iY << endl;
}

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

int Coordinate::getX()
{
	return m_iX;
}

int Coordinate::getY()
{
	return m_iY;
}

Line.h

#include "Coordinate.h"

class Line
{
public:
	Line(int x1, int y1, int x2, int y2);
	~Line();
	void printInfo();

private:
	Coordinate *m_pCoorA; //坐标类的对象指针
	Coordinate *m_pCoorB;
};

Line.cpp

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

Line::Line(int x1, int y1, int x2, int y2)
{
	m_pCoorA = new Coordinate(x1, y1);
	m_pCoorB = new Coordinate(x2, y2);
	cout << "Line() " << endl;
}

Line::~Line()
{
	delete m_pCoorA;
	m_pCoorA = NULL;
	delete m_pCoorB;
	m_pCoorB = NULL;
	cout << "~Line()" << endl;
}

void Line::printInfo()
{
	cout << "printInfo()" << endl;
	cout << "(" << m_pCoorA->getX() << "," << m_pCoorA->getY() << ")" << endl;
	cout << "(" << m_pCoorB->getX() << "," <<  m_pCoorB->getY() << ")" << endl;
}

demo.cpp

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

int main()
{
	Line *p = new Line(1, 2, 3, 4);
	p->printInfo();
	delete p;
	p = NULL;

	cout << sizeof(p) << endl; //一个int指针占4个内存单元
	cout << sizeof(Line) << endl; //有两个指针,占8个内存单元
	return 0;
}

this指针

如果参数与数据成员同名:
在这里插入图片描述
无法分辨哪个给哪个赋值

this指针是指向自身数据的指针

在这里插入图片描述
this放在哪里,表达的就是哪个的地址

则上述可如下表示:
在这里插入图片描述
是将参数len赋值给数据成员len
这样也可以使用与数据成员同名的参数进行表达

成员函数如何访问到对应的数据成员:
在这里插入图片描述
在这里插入图片描述
在实例化对象并使用成员函数时,this指针代表该对象本身的地址。例如,当去实例化arr1时,在构造函数中传入参数this,当执行给len赋值10这句时,就相当于执行给this->len 赋值10,因为this指的是arr1,this->len就相当于arr1的len

编码示例:
Array.h

class Array
{
public:
	Array(int len);
	~Array();
	Array* setLen(int len);
	int getLen();
	Array& printInfo();
private:
	int len;
};

Array.cpp

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

Array::Array(int len) 
{
	this->len = len;
}

Array::~Array()
{
	
}
Array* Array::setLen(int len)
{
	this->len = len;
	return this;
}
int Array::getLen()
{
	return len;
}
Array& Array::printInfo() 
{
	cout<<"len=" << len << endl;
	cout << this << endl;
	return *this;
}

demo.cpp

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

int main()
{
	Array arr1(10);
	arr1.printInfo().setLen(5)->printInfo();//分别通过引用和指针的方式
	//cout << "len=" << arr1.getLen() << endl;

	Array arr2(20);
	arr2.printInfo();
	cout << &arr2 << endl; //跟arr2.printInfo();输出一样,说明this指针指的是所在对象本身的地址
	return 0;
}

this指针无需用户定义,是编译器自动产生的。

对象各自的this指针指向各自对象的首地址,所以不同对象的this指针一定指向不同的内存地址

当成员函数的参数或临时变量与数据成员同名时,可以使用this指针区分同名的数据成员。

this指针也是指针类型,所以在32位编译器下也占用4个基本的内存单元,即sizeof(this)的结果为4。

常对象成员

定义:
在这里插入图片描述
初始化:(用初始化列表)
在这里插入图片描述
使用:
在这里插入图片描述

常成员函数

在这里插入图片描述

常成员函数中不能修改数据成员的值

普通成员函数:
在这里插入图片描述
常成员函数:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
上图调用的是普通的changeX()

在这里插入图片描述
上图调用的是changeX() const
实例化对象时加const,这样的对象叫做常对象

编码示例:
Coordinate.h

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

Coordinate.cpp

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

Coordinate::Coordinate(int x,int y)
{
	m_iX = x;
	m_iY = y;
	cout << "Coordinate()  " << m_iX << "," << m_iY << endl;
}

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

void Coordinate::setX(int x)
{
	m_iX = x;
}

int Coordinate::getX() const
{
	return m_iX;
}

void Coordinate::setY(int y)
{
	m_iY = y;
}

int Coordinate::getY() const
{
	return m_iY;
}

Line.h

#include "Coordinate.h"

class Line
{
public:
	Line(int x1, int y1, int x2, int y2);
	~Line();
	void setA(int x, int y);
	void setB(int x, int y);
	void printInfo();
	void printInfo() const;

private:
	const Coordinate m_CoorA; //也可以写成:Coordinate const m_CoorA;
	Coordinate m_CoorB;
};

Line.cpp

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

Line::Line(int x1, int y1, int x2, int y2) :m_CoorA(x1,y1),m_CoorB(x2,y2)
{
	cout << "Line()" << endl;
}

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

void Line::setA(int x, int y)
{

}

void Line::setB(int x, int y)
{
	m_CoorB.setX(x);
	m_CoorB.setY(y);
}

void Line::printInfo()
{
	cout << "printInfo()" << endl;
	cout << "(" << m_CoorA.getX() << "," << m_CoorA.getY() << ")" << endl;
	cout << "(" << m_CoorB.getX() << "," <<  m_CoorB.getY() << ")" << endl;
}

void Line::printInfo() const
{
	cout << "printInfo() const" << endl;
	cout << "(" << m_CoorA.getX() << "," << m_CoorA.getY() << ")" << endl;
	cout << "(" << m_CoorB.getX() << "," << m_CoorB.getY() << ")" << endl;
}

demo.cpp

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

int main()
{
	Line line(1, 2, 3, 4);
	line.printInfo();//调用的是普通成员函数
	const Line line2(5, 6, 7, 8);
	line2.printInfo();//调用的是常成员函数
	return 0;
}

要调用常成员函数,就在实例化对象时加const

对象的引用与对象的指针

在这里插入图片描述
上图,最后输出的都是coor1的(3,5)
&coor2是对象的引用
*pCoor是对象的指针

对象的常指针与对象的常引用

在这里插入图片描述
在这里插入图片描述
getX()和getY()不是常成员函数,()中的this,要求用的是有读写权限的,但coor2因为加了const,只有读权限,所以错误,只能调用常成员函数printInfo()

在这里插入图片描述
上图,const写在*后面,此时pCoor指向coor1之后,就不能再改为指向其他对象了,但指针所指对象的内容是可变的,所以该指针具有读写权限(只限于其所指向的对象可以读写)

常对象只能调用常成员函数,不能调用普通成员函数

普通对象能够调用常成员函数,也能够调用普通成员函数

常指针和常引用都只能调用对象的常成员函数。

对象引用和对象常引用都是对象的别名,一个对象可以有多个对象常引用。

举例:

#include <iostream>
using namespace std;
class Coordinate
{
    
public:
	Coordinate(int x, int y)
	{
		// 设置X,Y的坐标
		m_iX = x;
        m_iY = y;
	}
    // 实现常成员函数
	void printInfo() const
	{
	    cout << "(" << m_iX << "," << m_iY << ")" << endl;
	}
public:
	int m_iX;
	int m_iY;
};


int main(void)
{
	const Coordinate coor(3, 5);

	// 创建常指针p
	const Coordinate *p = &coor;
    // 创建常引用c
    const Coordinate &c = coor;
	
	coor.printInfo();
	p->printInfo();
	c.printInfo();  
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值