C++语言学习笔记10

拷贝构造函数

//	空类中编译器会提供一个默认的特殊的构造函数:拷贝构造函数
//	拷贝构造函数 函数名 当前类名 参数const 当前类的引用
// 	拷贝构造函数在用当前类的一个对象给另一个对象初始化的时候时被调用
//	参数中对象给this对象成员依次初始化
浅拷贝问题
  • 如果类中成员存在指针变量且new分配空间,调用拷贝构造函数的时候是值拷贝(地址拷贝)的过程,因此在析构回收空间时,同一个地址空间会被delete多次,导致程序崩溃。
  • 多个对象指针指向同一个空间,其一个对象修改里面的值,其他对象使用后的值是修改后的。如果实际确实需要多个对象共享,也不建议使用该方式,可以使用static静态成员变量。
#include <iostream>
using namespace std;

class CTest
{
public:
	int m_a;
	int* m_p;

public:

	CTest() {
		m_a = 10;
		m_p = new int(20);
	}

	CTest(int a) {
		m_a = a;
	}

	//	空类中编译器会提供一个默认的特殊的构造函数:拷贝构造函数
	//	拷贝构造函数 函数名 当前类名 参数const 当前类的引用
	// 	拷贝构造函数在用当前类的一个对象给另一个对象初始化的时候时被调用
	//	参数中对象给this对象成员依次初始化
	
	//	CTest(const CTest& tst) {
	//		this->m_a = tst.m_a;
	//	}


	~CTest() {
		if (m_p) {
			delete m_p;
			m_p = NULL;
		}
		//	默认拷贝构造函数的严重问题:浅拷贝问题
		//	浅拷贝问题的两点:
		//	1.如果类中成员存在指针变量且new分配空间,调用拷贝构造函数的时候是值拷贝(地址拷贝)的过程,因此在析构回收空间时,同一个地址空间会被delete多次,导致程序崩溃。
	}

};


int main() {

	//CTest tst(10);
	//CTest tst1(tst);
	//cout << tst1.m_a << endl;

	CTest tst;
	CTest tst1(tst);

	*tst1.m_p = 50;
	cout << *tst.m_p << endl;	
					//2.多个对象指针指向同一个空间,其一个对象修改里面的值,其他对象使用后的值是修改后的。
					//如果实际确实需要多个对象共享,也不建议使用该方式,可以使用static静态成员变量

	system("pause");
	return 0;
}
解决浅拷贝问题:深拷贝
//自写拷贝构造函数

	CTest(const CTest& tst) {
		this->m_a = tst.m_a;
		if (tst.m_p)
		{
			this->m_p = new int(*tst.m_p);
		}	//两个对象的m_p是new的不同地址,存的值是一样的
		else {
			this->m_p = NULL;
		}
	}
//整体程序

#include <iostream>
using namespace std;

class CTest
{
public:
	int m_a;
	int* m_p;
public:
	CTest() {
		m_a = 10;
		m_p = new int(20);
	}
	CTest(int a) {
		m_a = 20;
		m_p = NULL;
	}
	CTest(const CTest& tst) {
		this->m_a = tst.m_a;
		if (tst.m_p)
		{
			this->m_p = new int(*tst.m_p);
		}	//两个对象的m_p是new的不同地址,存的值是一样的
		else {
			this->m_p = NULL;
		}
	}
	~CTest() {
		if (m_p) {
			delete m_p;
			m_p = NULL;
	}
};

int main() {

	CTest tst1;
	CTest tst2(tst1);

	CTest tst3(10);
	CTest tst4(tst3);

	system("pause");
	return 0;
}

operator=(重载等号函数)

	//	编译期提供的默认的重载等号操作符 operator= 
	//	参数为const CTest& tst 返回值为const CTest&
	/*const CTest& operator=(const CTest& tst) {
		this->m_a = tst.m_a;
		return *this;
	}*/
	//	当前类的一个对象给另一个对象赋值的时候进行调用
	//	参数中对象给this对象成员依次赋值
	//	也存在浅拷贝问题,解决方法还是深拷贝
解决浅拷贝问题:深拷贝
	const CTest& operator=(const CTest& tst) {
		this->m_a = tst.m_a;
		if (tst.m_p)	
		{
			if (this->m_p) //this->m_p不一定有空间所以还要再判断
			{
				*this->m_p = *tst.m_p;
			}
			else {
				this->m_p = new int(*tst.m_p);
			}
		}
		else {
			if (this->m_p)
			{
				delete this->m_p;
				this->m_p = NULL;
				//其实tst.m_p没有空间也是NULL,所以写this->m_p = tst.m_p也可以~
			}
			else {
				this->m_p = tst.m_p;//两都没有空间 两都是空
			}
		}
		return *this;
	}
//整体程序
#include <iostream>
using namespace std;

class CTest
{
public:
	int m_a;
	int* m_p;

public:
	CTest() {
		m_a = 10;
		m_p = NULL;
	}
	CTest(int a) {
		m_a = a;
		m_p = new int(30);
	}

	int operator=(int a) {
		m_a = a;
		return m_a;
	}

	//	编译期提供的默认的重载等号操作符 operator= 
	//	参数为const CTest& tst 返回值为const CTest&
	/*const CTest& operator=(const CTest& tst) {
		this->m_a = tst.m_a;
		return *this;
	}*/
	//	当前类的一个对象给另一个对象赋值的时候进行调用
	//	参数中对象给this对象成员依次赋值
	//	也存在浅拷贝问题,解决方法还是深拷贝

	const CTest& operator=(const CTest& tst) {
		this->m_a = tst.m_a;
		if (tst.m_p)	
		{
			if (this->m_p) //this->m_p不一定有空间所以还要再判断
			{
				*this->m_p = *tst.m_p;
			}
			else {
				this->m_p = new int(*tst.m_p);
			}
		}
		else {
			if (this->m_p)
			{
				delete this->m_p;
				this->m_p = NULL;
				//其实tst.m_p没有空间也是NULL,所以写this->m_p = tst.m_p也可以~
			}
			else {
				this->m_p = tst.m_p;//两都没有空间 两都是空
			}
		}
		return *this;
	}

	~CTest() {
		if (m_p) 
		{
			delete m_p;
			m_p = NULL;
		}
	}
};

int main() {
	//CTest tst1;
	//CTest tst2;
	//tst2 = tst1 = 10;

	//CTest tst;
	//CTest tst3;
	//CTest tst4;
	//tst4 = tst3 = tst;

	CTest tst1;
	CTest tst2;		//都调用默认无参的构造,则m_p都是空,走的是this->m_p = tst.m_p;
	tst1 = tst2;

	CTest tst3(20);
	tst2 = tst3;	//有参的给无参的,走的是this->m_p = new int(*tst.m_p);

	CTest tst4(40);
	tst4 = tst3;	//都有空间,走的是*this->m_p = *tst.m_p;

	CTest tst5;
	tst5 = tst4;	//无空间的给有空间的赋值,走的是delete this->m_p;this->m_p = NULL;

	system("pause");
	return 0;
}

整理:空类中默认的函数(4个)

  • 默认无参构造函数
  • 默认析构函数
  • 默认拷贝构造函数
  • 默认operator=(重载等号操作符)

设计模式之单例模式

  • 1.当前类 有且只有 一个实例(对象)
    ----->类外不能定义对象 只能使用类内定义的一个对象
  • 2.能够自主创建这个实例(而不是调用者去创建实例)
  • 3.向全局开放这个实例
懒汉式
  • 要用到的时候再去创建唯一实例(时间换空间的做法,节省空间但消耗时间)
  • 多线程下要加锁,造成阻塞等待,只让一个线程进入m_pObjec = new CObject;的部分。
#include <iostream>
using namespace std;

class CObject
{
public:
	int m_a;
private:	//私有的 -----> 类外不能定义对象
	//static int ObjCount;	//计数器	
	static CObject* m_pObjec;
private:
	CObject() {
		m_a = 10;
	}
	CObject(const CObject& obj) {
		this->m_a = obj.m_a;
	}
	~CObject() {
	
	}	//PS:编译器提供的默认的是公有的~
public:
	static CObject* GetInstance() 	
		//设置一个接口 -----> 类外可以使用类内的唯一对象
		//					(static 可以不通过对象进行调用)
		//采用CObject* -----> 指针的形式通过地址来来使用 
		//不建议采用CObject -----> 因为临时对象有可能对程序造成一定影响
	{	
		//if (ObjCount >= 1) 
		if (m_pObjec) 
		{
			//return NULL;
			return m_pObjec;
		}
		//ObjCount++;
		m_pObjec = new CObject;	
        	//单线程下无问题,但多线程会出现问题,其解决方法是在前后加一个锁,造成阻塞等待,只有一个线程能进行这个部分。
		return m_pObjec;
	}
	static void Destory(CObject*& p) {
		if (m_pObjec) {
			delete m_pObjec;
			m_pObjec = NULL;
		}
		p = NULL;
	}
};

//int CObject::ObjCount = 0;	//静态成员类外初始化
CObject* CObject::m_pObjec = NULL;
//两个静态成员的作用都是为了去建议有没有创建多个对象,所以可以进行优化,注释掉ObjCount,将if判断中的内容进行修改,看m_pObjec是否为空即可

int main() {

	//CObject* pObject1 = CObject::GetInstance();
	//CObject* pObject2 = CObject::GetInstance();	
			//	情况:pObject1和pObject2分别具有不同的地址
			//	问题1:可以多次调用GetInstance以获得多个对象……
			//	改进方法:加一个计数器以统计当前实例有多少个对象	见行12及27-31行

			//	改进后情况:pObject1具有地址,pObject2为NULL
			//	问题2:违反“向全局开放这个实例”,pObject1和pObject2应该获取到相同地址才对
			//	改进方法:利用ObjCount进行置0

	CObject* pObject1 = CObject::GetInstance();
	//	ObjCount = 0;
	CObject* pObject2 = CObject::GetInstance();

			//	此时pObject1和pObject2分别具有不同的地址…说明还是有问题
			//	解决方法:ObjCount不能是全局变量,换成类内私有的静态成员变量,静态成员变量在类外进行初始化,此时ObjCount在目前函数内就不能使用了。
	
			//	但问题仍未解决,如何让其满足“1.	当前类 有且只有 一个实例(对象)”且满足“3.	向全局开放这个实例”?也就是说满足一对多的形式。
			//	前面的if (ObjCount >= 1) { return NULL; }这个部分就存在问题,不能返回空。一对多的形式可以借助静态成员变量来实现。

	//CObject obj1 = *pObject1;	
			//obj1的地址和pObject1/pObject2的地址不同,说明还是可以创建多个对象…
			//此处实际为对象给对象初始化,其调用的是拷贝构造函数
			//解决方法:将析构函数写为私有的,那么obj1生命周期结束时无法调用析构,则该句无法正常运行会报错。(但实际上,拷贝构造也可以解决这个问题,但尽量把拷贝构造和析构都加上)

	//CObject* pobj3 = new CObject(*pObject1);	
	//delete pobj3;	//虽然78行没有报错 但是delete调用析构时还是会报错
			//但如果不使用delete,78行依然可以正常运行,pobj3对象的地址不同于pObject1,违反了单例模式,其解决方法是:将拷贝构造也加上到私有的。

	pObject1->m_a = 20;
	cout << pObject2->m_a << endl;

	//delete pObject1;
			//问题:该实例最终销毁时由于析构私有,所以无法删除,所以还需要提供一个删除的方法。

	//CObject::Destory();	
	//cout << pObject2->m_a << endl;
			//类成员的指针确实置空了,但pObject1和pObject2没有被置空,只是pObject1和pObject2无法再使用里面的成员了(输出的不是想要的二值…)。
			//解决方式:Destory函数加入一个参数

	CObject::Destory(pObject1);	
	CObject::Destory(pObject2);

	if (pObject2)
	{
		cout << pObject2->m_a << endl;
	}

	system("pause");
	return 0;
}
饥汉式
  • 不管用与否,程序创建之初就把唯一实例创建出来(空间换时间的做法,不需要现new节省了时间,但从始至终一直占用空间)
  • 多线程下没问题。CObject* CObject::m_pObjec = new CObject;只会在程序运行初的编译期运行一次。
#include <iostream>
using namespace std;

class CObject
{
public:
	int m_a;
private:
	static CObject* m_pObjec;
private:
	CObject() {
		m_a = 10;
	}
	CObject(const CObject& obj) {
		this->m_a = obj.m_a;
	}
	~CObject() {
	
	}
public:
	static CObject* GetInstance() {
		return m_pObjec;
	}
};

CObject* CObject::m_pObjec = new CObject;

int main(){

	CObject* p1 = CObject::GetInstance();
	CObject* p2 = CObject::GetInstance();

	p1->m_a = 30;
	cout << p2->m_a << endl;

	system("pause");
	return 0;
}

设计模式之模板方法模式

  • 将不变的流程(步骤)和可变的实现(细节)分开,将公共的流程抽离到父类形成模板,具体的实现利用多态在子类中完成实现。
#include <iostream>
using namespace std;

class CPeople
{
public:
	virtual void EatBehavior() = 0;

	virtual void EatSytle() = 0;

	void eat() {
		cout << "1.盛一碗饭" << endl;
		EatBehavior();
		EatSytle();
		cout << "4.放嘴里嚼" << endl;
		cout << "5.吞肚子里" << endl;
		cout << "6.吃完饭了" << endl;
	}
};

class CChina:public CPeople
{
public:
	void EatBehavior() {
		cout << "2.端起来碗" << endl;
	}
	void EatSytle() {
		cout << "3.筷子扒拉" << endl;
	}
	//void eat() {
	//	cout << "1.盛一碗饭" << endl;
	//	EatBehavior();
	//	EatSytle();
	//	cout << "4.放嘴里嚼" << endl;
	//	cout << "5.吞肚子里" << endl;
	//	cout << "6.吃完饭了" << endl;
	//}
};

class CAmerican :public CPeople
{
public:
	void EatBehavior() {
		cout << "2.嘴靠上去" << endl;
	}
	void EatSytle() {
		cout << "3.刀叉扒拉" << endl;
	}
	//void eat() {
	//	cout << "1.盛一碗饭" << endl;
	//	EatBehavior();
	//	EatSytle();
	//	cout << "4.放嘴里嚼" << endl;
	//	cout << "5.吞肚子里" << endl;
	//	cout << "6.吃完饭了" << endl;
	//}
};

class CIndian :public CPeople
{
public:
	void EatBehavior() {
		cout << "2.嘴靠上去" << endl;
	}
	void EatSytle() {
		cout << "3.用手抓饭" << endl;
	}
	//void eat() {
	//	cout << "1.盛一碗饭" << endl;
	//	EatBehavior();
	//	EatSytle();
	//	cout << "4.放嘴里嚼" << endl;
	//	cout << "5.吞肚子里" << endl;
	//	cout << "6.吃完饭了" << endl;
	//}
};

int main() {

	CPeople* pPeo1 = new CChina;
	pPeo1->eat();

	CPeople* pPeo2 = new CAmerican;
	pPeo2->eat();

	CPeople* pPeo3 = new CIndian;
	pPeo3->eat();

	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

97Marcus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值