C++ 特殊类设计

这篇具有很好参考价值的文章主要介绍了C++ 特殊类设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

设计一个只能在堆上创建对象的类

如果只能在堆上创建对象,就是限制在栈上和静态区中创建对象,步骤如下:

方法一:将构造函数私有化.

  • 将构造函数设置为私有,以防用户在栈上或者静态区直接调用构造函数创建对象.
  • 在公有区域提供一个静态的成员函数,在该静态成员函数中使用new创建对象,此时因为new在成员函数中,可以调用私有区域的构造函数.
  • 限制外部利用拷贝构造在栈上创建对象,我们可以采用C++11中的delete关键字来限制编译器默认自动产生拷贝构造函数,
class HeapOnly
{
public:
	//方法一:提供一个获取对象的接口,并且该接口必须设置为静态成员函数
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}
	void Delete()
	{
		cout << "Delete()" << endl;
		delete this;
	}
	//限制使用拷贝构造,赋值拷贝.
	HeapOnly(const HeapOnly& hp) = delete;
	HeapOnly& operator=(const HeapOnly& hp) = delete;
private:
	HeapOnly()
		:_a(0)
	{}
	int _a;
	//C++11
	//HeapOnly(const HeapOnly&) = delete;
};
int main()
{
	//限制在栈上创建.
	/*HeapOnly hp1;
	
	//限制在静态区创建.
	static HeapOnly hp2;*/

	HeapOnly* ptr = HeapOnly::CreateObj();

   ptr->Delete();
}

注意:

  • 我们提供的CreateObj()函数一定要设置为静态函数,因为非静态成员必须通过对象才能调用,而创建对象又必须通过CreateObj()函数创建.(以下写法类似)

方法二:将析构函数私有化

该方法又分为两种:
1.使用静态成员函数调用析构.
2:公有写Delete()函数,利用this指针析构.

class HeapOnly
{
public:
	static void Delete(HeapOnly* ptr)
	{
		cout << "static Delete " << endl;
		delete ptr;
	}
	void Delete()
	{
		cout << "Delete()" << endl;
		delete this;
	}
private:
	~HeapOnly()
	{}
	int _a;
};
int main()
{
	//HeapOnly hp1;
	
	HeapOnly* ptr = new HeapOnly;
    //1.
    HeapOnly::Delete(ptr);
    //2.
	ptr->Delete();
}

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

只能在栈上创建对象,就是限制在静态区和堆上创建对象.思路和上一个设计一样;

  • 将构造函数设置为私有,以外部在堆或者静态区中直接调用构造函数创建对象.
  • 在公有区域提供一个静态的成员函数,在该静态成员函数中以在栈区的形式创造对象并返回,因为在类中不受私有区域的限制,可以访问类的构造函数.

class StackOnly
{
public:
	//2、提供一个获取对象的接口,并且该接口必须设置为静态成员函数
	static StackOnly CreateObj()
	{
		StackOnly st;
		return st;
     }
	//不可以限制拷贝构造.
	//StackOnly(const StackOnly& st) = delete;
	//StackOnly& operator=(const StackOnly& st) = delete;
private:
	//1、将构造函数设置为私有
	StackOnly()
		:_a(0)
	{}
	int _a;
};

该方法的缺点就是无法防止外部调用编译器默认生成的拷贝构造函数创建对象.

StackOnly st1 = StackOnly::CreateObj();
static StackOnly st2(st1); //在静态区中拷贝构造对象st1
StackOnly* ptr = new StackOnly(st1); //在堆上拷贝构造对象st1

注意:

  • 不可以限制拷贝构造,因为CreateObj函数中创建对象为临时对象,因此必须使用传值返回,而传值拷贝一定会调用拷贝构造.

针对于在堆上拷贝构造对象,我们可以采用限制new的方式.

class StackOnly
{
public:
	//2、提供一个获取对象的接口,并且该接口必须设置为静态成员函数
	static StackOnly CreateObj()
	{
		StackOnly st;
		return st;
     }
     //限制nwe.
    void* operator new(size_t n) = delete;
private:
	//1、将构造函数设置为私有
	StackOnly()
		:_a(0)
	{}
	int _a;
};

解释:
因为new创建对象主要分为两步:
1.调用operator new 函数创建空间.
2.调用构造函数完成对对象的初始化.

综合以上解释,我们将operator new进行删除,就是限制了new的使用,进而限制了外部利用拷贝构造在堆中创建对象了,但是我们依旧无法解决如何在静态区中利用拷贝构造创建对象.(一般在实际中也很少遇见).

设计一个不能被拷贝的类

如果想让一个类禁止拷贝,我们只需要让该类不能调用拷贝构造以及赋值运算符重载即可.

  • 在C++98中,将拷贝构造函数与赋值重载只声明不定义,并将访问权限设置为私有.
  • 而在C++11中,通常在默认成员函数中加入跟上=delete,就代表让编译i其删除掉该默认成员函数.
class CopyBan
{
public:
	CopyBan()
	{}
private:
	//C++98
	CopyBan(const CopyBan&);
	CopyBan& operator=(const CopyBan&);
	//C++11
	//CopyBan(const CopyBan&) = delete;
	//CopyBan& operator=(const CopyBan&) = delete;
};

设计一个不能被继承的类

C++98

在C++98中,将该类的构造函数设置为私有,因为如果派生类继承了基类的私有成员,就意味着必须调用基类的构造函数对基类的私有成员初始化,但是基类的构造函数设置为私有,就意味派生类无法访问到基类的构造函数,所以该类被继承后也就无法创建出对象.

class NonInherit
{
public:
	static NonInherit CreateObj()
	{
		return NonInherit();
	}
private:
	//将构造函数设置为私有
	NonInherit()
	{}
};

C++11

因为C++98中将基类的构造函数设置为私有而导致无法被派生类继承实际上依旧可可以编译成功,不能将派生类实例化.于是,C++11中新增了final关键字,使用final修饰类表示该类无法被继承,如果强行继承了,也无法编译成功.`

//基类
class Base  final
{
private:
	Base()
		:_a(0)
	{}
protected:
	int _a;
};
//派生类
class Derived : protected Base
{
public:
//
protected:
	int _a;
	int _b;
};

设计一个只能创建一个对象的类(单例模式)

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

单例模式有两种实现模式:

饿汉模式

饿汉模式指的是:无论用户将来使用或者不使用,当程序启动时就创建一个唯一的实例对象,简单来说,就是在main函数之前就创建了对象.

饿汉模式的实现步骤如下:

  • 将构造函数设置为私有,并将拷贝构造和运算符重载删除,防止外部利用拷贝构造创建对象.
  • 提供一个指向单例对象的一个静态指针,在main函数之前完成对单例对象的初始化.
  • 使用静态成员函数获取单例对象,也称为全局访问点.
 class Singleton
{
public:
	static Singleton* GetInstance()
	{
		return _pList;
	}
	void* Aoll()
	{
		cout << "Aoll" << endl;
		return _pinst;
	}
	void Dell(void* ptr)
	{
		cout << "Dell" << endl;
	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	MemoryPool()
	{//}
	//静态成员.
	static MemoryPool* _pinst;
};
//类外定义
MemoryPool* MemoryPool::_pinst = new MemoryPool;

int main()
{
	MemoryPool* ptr1 = MemoryPool::GetInstance();
    //通过对象指针调用函数.
	ptr1->Dell(ptr1);
}

注意:
静态成员定义时就是在main函数之前就定义好了,所以我们必须通过类中静态成员函数将该类唯一的实例对象在外部拿到.

优点:简单,没有线程安全问题.

缺点:

  1. 一个程序中,多个单例,并且有先后创建初始化顺序要求时.饿汉模式无法控制,因为静态成员谁先初始化谁后初始化不能确定.比如,程序两个单例类A和B,假设要求A先创建初始化,B再创建初始化.

  2. 饿汉单例类,初始化时任务多,会影响程序启动速度.

懒汉模式

懒汉模式指: 只有第一次使用实例对象时,才创建对象.

懒汉模式实现步骤如下:

  • 将构造函数设置为私有,并将拷贝构造和运算符重载删除,防止外部利用拷贝构造创建对象.

  • 提供一个指向单例对象的static指针,并在程序入口之前先将其初始化为空。

  • 使用静态成员函数获取单例对象,也称为全局访问点.如果为static 指针为空,就说明第一次使用该实例对象需要重新创建获取,如果不为空,则依旧返回原来的实例对象.

class Singleton
{
public:
	//da
	static Singleton* GetInstance()
	{
		if (_pList == nullptr)
		{
			return _pList = new  Singleton;
		}
		return _pList;
	}
	void* Aoll()
	{
		cout << "Aoll" << endl;
		return _pList;
	}
	void Dell(void* ptr)
	{
		cout << "Dell" << endl;
	}
private:
	Singleton()
	{
	}
	static  Singleton* _pList;
};
Singleton* Singleton::_pList = nullptr;

优点:

  • 可以控制初始化顺序,比如一个类中有多个关于懒汉模式的类的内置成员, 我们可以先对内置成员_a调用getinstance,再对内置成员_b调用getInstance,进而让_a先初始化,_b后初始化.

  • 不会影响程序启动速度,因为内置成员后初始化.

缺点:

  • 相对复杂(线程安全问题),这个以后我们深度学习.

单例模式对象释放问题

1: 一般情况下,单例对象不需要释放的,一般整个程序运行期间都会用到他.

2: 单例对象在进程正常结束后,也会资源释放.(除非因为程序错误变为僵尸进程).

3: 有些特殊场景需要释放,比如单例对象析构时,要进行一些持久化操作((将内中的数据写进文件,数据库写).

4: 如果我们直接通过GetInstance()获取实例对象指针然后使用delete删除,这样不仅丑陋,还很容易被忽视.再接下来中我们无法确定外部是否会再调用GetInstance()获取该指针.

所以,针对于单例对象,我们常用了内嵌类的形式,让程序结束时符合相关条件自动销毁.

//垃圾回收类
class CGarbo
{
public:
	~CGarbo()
	{
		if (_inst != nullptr)
		{
			delete _inst;
			_inst = nullptr;
		}
	}
};
//定义一个静态成员变量,在程序结束时,系统会自动调用它的析构函数,从而释放单例对象.
static CGarbo Garbo;

注意:
我们必须通过内嵌类的形式,析构单例类,而不能在单例类的析构函数中使用delete进行资源释放,因为delete又会调用单例类的析构函数,而析构函数中又是使用delete进行资源释放,如此递归删除.文章来源地址https://www.toymoban.com/news/detail-515647.html

到了这里,关于C++ 特殊类设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++学习笔记】特殊类设计

    C++类中只有两个拷贝的方式:拷贝构造函数和赋值运算符重载。想要设计一个不能被拷贝的类,只要想办法让类中这两种拷贝的方式不能使用即可。 因为在C++的类中,这两种拷贝的方式是默认存在的,所以需要在类中只声明不定义即可。 但是由于在用户在类外还能定义,所

    2024年02月15日
    浏览(46)
  • 【C++】特殊类的设计

    💕 C++98方式: 在C++11之前,想要一个一个类不被拷贝,只有将 拷贝构造函数 定义为私有,这样在类外就不能调用拷贝构造函数来构造对象了。但是在类内还是可以调用拷贝构造函数来构造对象。 所以正确的做法是 将拷贝构造函数定义为私有,同时拷贝构造函数只声明,不

    2024年02月07日
    浏览(45)
  • C++进阶(十六)特殊类设计

    📘北尘_ :个人主页 🌎个人专栏 :《Linux操作系统》《经典算法试题 》《C++》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 拷贝只会放生在两个场景中: 拷贝构造函数以及赋值运算符重载 ,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运

    2024年02月19日
    浏览(33)
  • c++学习(特殊类设计)[30]

    如果你想要确保对象只能在堆上创建,可以通过将析构函数声明为私有,并提供一个静态成员函数来创建对象。这样,类的实例化只能通过调用静态成员函数来完成,而无法直接在栈上创建对象。 以下是一个示例: 在上面的示例中, HeapOnlyClass 类的构造函数和析构函数都被

    2024年02月14日
    浏览(35)
  • C++语法(26)--- 特殊类设计

    C++语法(25)--- 异常与智能指针_哈里沃克的博客-CSDN博客 https://blog.csdn.net/m0_63488627/article/details/131537799?spm=1001.2014.3001.5501   目录 1.特殊类设计 1.设计一个类,不能被拷贝 C++98 C++11 2.设计一个类,只能在堆上创建对象 1.封掉所有拷贝 C++98 C++11 2.封掉析构函数 3.设计一个类,只能

    2024年02月15日
    浏览(64)
  • C++特殊类设计&&类型转换

    在普通类的设计基础上,提出一些限制条件设计的类就是特殊类。 拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝, 只需让该类 不能调用拷贝构造函数 以及 赋值运算符重载 即可。  C++98中的方式: 将拷贝构造函数与赋值运算符

    2024年01月16日
    浏览(48)
  • 【高级程序设计语言C++】特殊类设计

    拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 用C++11的话,可以使用特殊的语法来实现一个不能被拷贝的类。在C++11中,可以使用删除函数(deleted function)来禁用拷

    2024年02月10日
    浏览(51)
  • 【C++修炼之路】33.特殊类设计

    每一个不曾起舞的日子都是对生命的辜负 掌握常见特殊类的设计方式 拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C++98 将拷贝构造函数与赋值运算符重载只声明不

    2024年02月13日
    浏览(53)
  • C++进阶 特殊类的设计

    本篇博客介绍:介绍几种特殊的类 我们的拷贝只会发生在两个场景当中 拷贝构造函数 赋值运算符重载 所以说我们只需要让类失去 或者说不能使用这两个函数即可 这里有两个解决方案 在C++98中 我们将拷贝构造函数只声明不定义 并且将其访问权限设置为私有即可 原因如下

    2024年02月12日
    浏览(40)
  • C++之特殊类的设计

    目录 一、单例模式 1、设计模式 2、单例模式 1、饿汉模式 2、懒汉模式 3、单例对象的释放问题 二、设计一个不能被拷贝的类 三、设计一个只能在堆上创建对象的类 四、设计一个只能在栈上创建对象的类 五、设计一个不能被继承的类 概念: 设计模式(Design Pattern)是一套被

    2024年02月08日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包