特殊类的设计

这个设计模式细节挺多,每个模式感觉好像能大致回想起来,但是总有遗漏,所以打算写一篇文章来复习一遍。

一 CopyBan

设计一个防拷贝的类

class CopyBan  不能被拷贝
{
public:
	CopyBan()可以构造
	{
		cout<<"CopyBan()"<

还有种防拷贝的方式是如下的,

class CopyBan
{

private:
	CopyBan(const CopyBan& fc);//防拷贝2,只声明,不实现

};

虽然外部调用拷贝构造时会出错,但要编译才报错,如果直接用delete就可以在写代码阶段就报红了
设为私有是为了防止外部调用,但防不住别人去实现,实现也没用
CopyBan::CopyBan(const CopyBan& fc)
{
	cout<<"CopyBan::CopyBan(const CopyBan& fc)";
}

二 StackOnly

设计一个只能在栈上创建的类。

class StackOnly  
{

	StackOnly()
	{
		cout << "StackOnly()" << endl;
	}

	StackOnly(const StackOnly& s)拷贝构造可用于栈上创建对象
	{
		cout << "StackOnly(const StackOnly& s)" << endl;
	}

	void* operator new(size_t size) = delete;

	new = operator new + 构造, new可以调用拷贝和普通构造, 所以只能封operator new
		
    析构不能封
    ~StackOnly()
	{
		cout << "~StackOnly()" << endl;
	}
    析构函数一封,在栈上都无法创建对象,会直接报红
};

   所以说new在最上层,其实是个关键字,可以申请空间加调用构造函数初始化,而内部封装了operator new这个函数,用于申请空间,调用构造函数有其它函数完成,此时我们在类内重载一个operator new,调用时先用类内的,而类内的已被删除,编译器选择直接报错,而不是去类外找全局的operator new,显示调用全局的用::operator new。?而且静态变量这个也防不住。

三 HeapOnly

只能在堆上创建 

class HeapOnly
{
public:
	void Delete()
	{
		delete this;
		cout << "Delete";
	}
	HeapOnly()
	{
		cout << "HeapOnly()" << endl;
	}
	HeapOnly(const HeapOnly& s)
	{
		cout << "HeapOnly(const HeapOnly& s)" << endl;
	}
	//封析构
private:
	~HeapOnly()
	{
		cout<<"~HeapOnly()"<

  方法2,在栈上创建对象无非就是用构造和拷贝构造函数,如果我们把这两个封了,就不能在栈上创建对象了。封了构造函数,那new也就没办法调用构造函数了,所以必须提供一个公有函数,在该函数内用new, 这样new可以调用构造函数,我们也可以调用这个公有函数来创建对象。好,问题来了,如何调用这个公有函数,用对象调,对象哪里来,调用Creat这个公有函数创建对象呗,这不就逻辑死循环了吗?所以这个公有函数就必须是静态的。析构就可以不用private修饰了。

class HeapOnly2
{
public:
	static HeapOnly2* Creat()
	{
		return new HeapOnly2;
	}
	~HeapOnly2()
	{
		cout << "~HeapOnly2()" << endl;
	}
private:
	HeapOnly2()
	{
		cout << "HeapOnly2()" << endl;
	}
	HeapOnly2(const HeapOnly2& s)
	{
		cout << "HeapOnly2(const HeapOnly2& s)" << endl;
	}
};

四 单例模式之饿汉模式

   单例模式是指该类只实例化一个对象,那构造得设为private,免得让外部创建对象,那我们如何创建那个唯一的对象呢,如果创建一个全局对象,然后写一个静态函数Creat函数,返回这个对象的指针或者引用就好了,可是构造函数私有了,外部无法调用。所以还得在类内创建对象,大佬是将其设为静态成员变量。这样编译前sgl成员就会被创建。至于Creat函数是返回指针还是引用都可以。

class SingleHungry
{
public:
	static SingleHungry* Creat()
	{
		return &sgl;
	}
 
private:

    析构可以设为似有,由于静态成员变量是类内的,可以调用private修饰的     
    ~SingleHungry()
	{
		cout<<"~SingleHungry()"<

  sg1属于类内的成员, 所以定义时可以调用private修饰的构造函数,原来我以为是这样解释的,可回想好像不太对,类内成员(例如存个该类类型的指针)在类外也调用不了private修饰的函数啊,可是当一个静态成员函数的定义和声明分离,静态成员函数的定义内是可以调用private修饰的成员函数的,所以我认为sg1可以调用构造函数的原因和这个是类似的,祖师爷也没办法了,只能做这种特殊处理了。
 

五 单例模式之懒汉模式

   饿汉模式是定义一个静态变量,之后要使用这个类,就先调用一个Creat返回这个静态对象的指针或者引用,但是如果程序存在大量的静态变量,那在启动的时候就需要做非常多的初始化工作,一个程序启动可能要半个小时,而且最关键的是无法决定静态变量的初始化顺序,有时候B类依赖A类初始化,但是有可能B类静态对象初始化时,A类还未创建,这就出问题。

所以我们一开始就先不创建对象,而是定义一个静态对象的指针,而且把指针初始化为空,这个时候初始化指针还不简单吗。那什么时候创建对象呢?调用Creat函数的时候就创建对象给sgl指针。

这样也就可以确定静态对象的创建顺序了,可是还有个问题就是sgl析构的问题。这个指针指向的资源如何释放,有人说,没事调用析构函数,大坑 !  sgl是new返回的,之所以要调用delete有两个作用,一个是释放sgl指向的空间,还有就是调用析构函数释放SingleLazy的对象内部的资源,举个例子,例如 string* p new string ;  delete不仅要释放p指向的这个string对象,析构函数是释放内部指针指向的资源的,所以不能sgl不能直接调用析构函数。要用delete。delete可以,但是有时候例如我们除了释放内存的资源外,可能还想将保存到磁盘上,那就不能单单写个delete,所以我们再写一个函数Destroy()去封装delete,并且在delete前把资源保存到磁盘。

class SingleLazy
{
public:
	static SingleLazy* Creat()
	{
		sgl = new SingleLazy();
		return sgl;
	}
	static void Destroy()
	{
		string s("he");
		FILE* f = fopen("test.txt","w");
		fwrite(s.c_str(),sizeof(string),1,f);
		delete sgl;
	}
private:
	SingleLazy()
	{
		cout << "SingleLazy()" << endl;
	}
	~SingleLazy()
	{
		cout << "~SingleLazy" << endl;
	}

	SingleLazy(const SingleLazy& sh) = delete;
	SingleLazy operator=(const SingleLazy& sh) = delete;
	static SingleLazy* sgl;
};
SingleLazy* SingleLazy::sgl = nullptr;

void test9()
{
	
    SingleLazy*p=SingleLazy::Creat();
          	
	SingleLazy::Destroy(); 但是还有个缺点,就是释放还是要手动释放,
                       特别是当有十几二十个单例对象的时候,一个个Destroy会麻烦
                           能不能智能一点呢
                            
}

    噢,智能指针,用智能指针,但是智能指针一般是程序结束后自动调用析构的,我们显示调用也不是不可以,但是智能指针的析构函数就得把指针置空,不然程序结束还会再调用,就二次析构了,我感觉用智能指针应该是可以的。还有第二种办法就是再写一个静态对象(不能是普通,不然会逻辑死循环),当程序结束,这个静态成员也就要调用析构函数释放了,我们在函数内把单例对象的释放方法放入,也就实现了智能化的释放资源。

class Garbage Garbage是SingleLazy的内部类,方便访问sgl变量
	{
	public:
		~Garbage()
		{
            if(SingleLazy::sgl)
			    delete SingleLazy::sgl;
		}
	};
private:

	static  Garbage gb;  gb是SingleLazy成员变量,也可以写到全局,这个时候是不是静态就无所谓了

说完懒汉模式后,我再提一种自己的,根据饿汉模式优化的。而且还不用操心释放的问题。

class SingleHungry
{
public:
	static SingleHungry& Creat()
	{
		static SingleHungry sgl;
		return sgl;
	}
	
private:
	~SingleHungry()
	{
		cout << "~SingleHungry()" << endl;
	}
	SingleHungry()
	{
		cout << "SingleHungry()" << endl;
	}
	SingleHungry(const SingleHungry& sh) = delete;
	SingleHungry operator=(const SingleHungry& sh) = delete;
};

六 NonInherit

设计一个类不能被继承,最后的方法就是在该类名后加个final,这是c++11支持的。

   而c++98则是将该类构造函数设为私有,这样派生类就调不到基类的构造函数,而且你会发现这个时候派生类的默认构造函数被delete了,感觉可能是编译器发现自己的默认构造初始化不了自己的成员,就气急败坏地删了,还有更多情况会被delete,有兴趣可了解了解。

你可能感兴趣的:(c++,c++)