【C++】特殊类的设计

        本篇博客整理了一些常见面试题,通过诠释有部分限制条件的特殊类,更深入地探究C++中类和对象的运用。

目录

一、无法被拷贝的类

二、只能在堆上创建的类

三、只能在栈上创建的类

四、无法被继承的类

五、只能创建一个对象的类

1.饿汉模式

2.懒汉模式


一、无法被拷贝的类

        类和对象的拷贝主要跟类提供的拷贝构造和赋值重载有关,因此禁止拷贝构造函数和赋值重载就可以禁止类和对象的拷贝了。这在C++中主要有两种方案实现:   

        C++98提供了,将拷贝构造和赋值重载设置为私有且只声明不定义。

class CopyBan
{
public:
	CopyBan()
	{}
 
private:
    //将以下两个默认成员函数设置为私有且只声明不定义
	CopyBan(const CopyBan& cb);            // 拷贝构造
	CopyBan& operator=(const CopyBan& cb); // 赋值重载
    //1.将它们设置成私有,哪怕用户在类外定义了它们,也可以禁止类外的拷贝
    //2.只声明不定义,就可以让它们被调用的时候,
    //  由于没有定义而产生链接错误,在编译阶段就报错,禁止类内的拷贝
};

        C++11提供了扩展的关键字delete,可以让编译器禁用类中的默认成员函数。

class CopyBan
{
public:
	CopyBan()
	{}
 
private:
    //关键字delete可以让编译器禁止生成拷贝构造和赋值重载
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
};

二、只能在堆上创建的类

        正常创建对象时,一般是在栈上创建的,其间会调用相应的构造函数来初始化对象。

        已知new申请的资源是从堆上申请的,那么要设计一个只能在堆上创建的类,只需为对象定制一个构造的方案,让对象只能通过new来创建。方案如下:

        1、将析构函数私有化,为对象定制析构的方案,并通过new+默认构造来创建对象

class HeapOnly
{
public:
	void Destroy()
	{
		delete this;
	}
private:
	~HeapOnly()
	{
		//...
	}
};

int main()
{
	HeapOnly* hp = new HeapOnly;
	hp->Destroy();

	return 0;
}

        2、将构造函数私有化,通过静态成员函数定制对象构造的方案,且禁止拷贝和赋值:

class HeapOnly
{
public:
	static HeapOnly* CreateObj()//构造不能被直接调用,但可以被间接调用
	{
		return new HeapOnly;
	}
    //非静态成员函数都有一个默认参数this指针,它本质是一个HeapOnly对象
    //但构造函数设置为私有,无法创建这样一个对象,调用函数会陷入“先有鸡还是先有蛋”的问题(非静态成员函数调用需要传HeapOnly对象,但HeapOnly对象又只能调用非静态成员函数创建)
    //因为静态成员函数没有默认参数this指针,就无须先创建一个HeapOnly对象,避免了以上问题
    //所以此处选用静态成员函数来定制构造方案
    //调用时通过“类名::静态成员函数名”即可
private:
    //1. 将类的构造函数私有,拷贝构造和赋值重载声明成私有并禁用,防止别人调用拷贝在栈上生成对象。
    //2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
	HeapOnly()
	{
		//...
	}

	HeapOnly(const HeapOnly& hp) = delete;
	HeapOnly& operator=(const HeapOnly& hp) = delete;
};

int main()
{
	HeapOnly* hp = HeapOnly::CreateObj();//HeapOnly*类型的指针hp接收new的返回值
	//HeapOnly copy(*hp3); //不可拷贝


    delete hp;//hp不使用后,手动delete释放(这里会调用HeapOnly的默认析构)

	return 0;
}

三、只能在栈上创建的类

        一般创建对象时,本就是在栈上创建的,唯有通过new才能在堆上创建对象。所以,要设计只能在栈上创建的类,只需在类中防止new创建对象。

        将构造函数私有化,通过静态成员函数定制对象构造的方案,且禁用operator new:

class StackOnly
{
public:
    //将构造函数私有化,通过静态成员函数定制对象构造的方案
    //防止别人用new在堆上创建StackOnly对象
	static StackOnly CreateObj()
	{
		StackOnly st;
		return st;
	}
private:
	StackOnly()
	{
		//...
	}

	// 对一个类实现专属operator new,然后禁用它
    // 防止内类new创建对象
	void* operator new(size_t size) = delete;
};
int main()
{
	StackOnly hp1 = StackOnly::CreateObj();
	StackOnly copy(hp1);//允许通过默认拷贝构造进行拷贝

	// StackOnly* hp2 = new StackOnly(hp1);//但不允许new来创建对象
    // new的时候可以调构造也可以调拷贝构造
	//  new => operator new(默认调用全局的) + 构造

	return 0;
}

四、无法被继承的类

        C++中有两种方案来实现一个类无法被继承。

        C++98提供了,将构造函数私有化

//构造函数私有化,派生类中调不到基类的构造函数。则无法继承

class NonInherit //基类
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
	NonInherit() 
	{}
};
 
class N : public NonInherit // 生类
{};

int main()
{
    //N n; //一旦实例化就会报错

    return 0;
}

        C++11提供了关键字final

//final修饰类,表示该类不能被继承
class A  final
{
	// ....
};
class B : public A //编译时就会报错
{
	// ....
};

五、只能创建一个对象的类

        要设计只能创建一个对象的类,可以通过单例模式来实现。

        单例模式是一种设计模式。设计模式是指,一套被反复使用、大多数人知晓的、经过分类整理的、代码设计的经验总结,可以提高代码可重用性,让代码更容易被他人理解,保证代码可靠性。设计模式可以使代码编写真正工程化,是软件工程的经络。

        单例模式的作用是,可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,使该实例被所有程序模块共享。例如在某个服务器程序中,服务器的配置数据存放在一个文件,而这些配置数据由一个单例对象统一读取,服务进程中的其他对象可以通过这个单例对象来获取这些配置信息,这样就简化了在复杂环境下的配置管理。

        而它又有饿汉模式和懒汉模式——两种实现模式。

【Tips】单例模式的实现要点:

  1. 因为全局只能有一个对象,所以需要将构造函数私有化;
  2. 用一个static静态指针(类的成员变量之一,在类外初始化)管理实例化的单例对象,并且提供一个静态成员函数,以获取这个static静态指针;
  3. 禁止拷贝,保证全局只有一个单例对象;
  4. 可以使用互斥锁来保证数据读取时的线程安全(本篇博客在此不涉及)。

1.饿汉模式

        饿汉模式是指,在程序启动时(即main函数开始前)就实例化出单例对象

// 饿汉模式:一开始(main函数运行之前)就创建单例对象
//优点:实现起来更简单
//缺点:
// 1、如果单例对象初始化内容很多,影响启动速度
// 2、如果两个单例类互相有依赖关系,就会发生错误(假设有A B两个单例类,要求A先创建,B再创建,B的初始化创建依赖A)
namespace hungry
{
	class Singleton
	{
	public:
		// step2、提供获取单例对象的接口函数
		static Singleton& GetInstance()
		{
			return _sinst;
		}

	private:
		// step1、构造函数私有
		Singleton()
		{
			// ...
		}

		// step3、防拷贝
		Singleton(const Singleton& s) = delete;
		Singleton& operator=(const Singleton& s) = delete;
        
        //成员变量
		map<string, string> _dict;//管理配置数据
		static Singleton _sinst;  //指向单例对象的指针


	public:

		//单例类中可以添加各种函数

		void func();

		void Add(const pair<string, string>& kv)
		{
			_dict[kv.first] = kv.second;
		}

		void Print()
		{
			for (auto& e : _dict)
			{
				cout << e.first << ":" << e.second << endl;
			}
			cout << endl;
		}
	};

    //类外初始化
	Singleton Singleton::_sinst = new Singleton();;

	void Singleton::func()
	{
		_dict["xxx"] = "1111";
	}
}

2.懒汉模式

        懒汉模式是指,单例对象在第一次被需要使用时才实例化。如果单例对象的构造十分耗时,或者会占用很多资源(例如加载插件、初始化网络连接、读取文件等),为了不影响程序的正常启动,可以使用懒汉模式(或称延迟加载)。

//懒汉模式
//跟饿汉模式几乎相同,唯独不能在main函数运行前创建对象
//优点:
// 1、第一次使用单例对象时才创建对象,进程启动过程中无负载
// 2、多个互相依赖的单例可以控制启动顺序(通过代码顺序)
//缺点:
// 1、实现起来更复杂
// 2、存在线程安全问题
namespace lazy
{
	class Singleton
	{
	public:
		// step2、提供获取单例对象的接口函数
		static Singleton& GetInstance()
		{
			if (_psinst == nullptr)
			{
				// 第一次调用GetInstance的时候创建单例对象
				_psinst = new Singleton;
			}

			return *_psinst;
		}

        //【补】单例对象释放问题:
		// 一般不用释放,唯独特殊场景:
		// 1、中途需要显示释放  
        // 2、程序结束时,需要做一些特殊动作(如持久化)
		// 写法如下:
		static void DelInstance()
		{
			if (_psinst)
			{
				delete _psinst;
				_psinst = nullptr;
			}
		}

		//方式1:嵌在单例类内部来析构大量对象
		class GC
		{
		public:
			~GC()
			{
				lazy::Singleton::DelInstance();
			}
		};

	private:
		// step1、构造函数私有
		Singleton()
		{
			// ...
		}


		// step3、防拷贝
		Singleton(const Singleton& s) = delete;
		Singleton& operator=(const Singleton& s) = delete;

        //成员变量
		map<string, string> _dict;

		static Singleton* _psinst;//指向单例对象的指针(因为懒汉模式下的单例对象是new出来的)
		
        static GC _gc;            //内部类用以析构单例对象
	};
    //类外初始化
	Singleton* Singleton::_psinst = nullptr; //指针使对象不被调用就不会被创建
	Singleton::GC Singleton::_gc;
}

//方式2:写在单例类外部来析构大量对象
class GC
{
public:
	~GC()
	{
		lazy::Singleton::DelInstance();
	}
};

GC gc;

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值