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

我们分析,当一个类只允许在堆上创建对象,原本,正常创建对象一定会调用构造函数,或者拷贝构造,要使用构造函数或者拷贝构造去创建对象(别人调用拷贝构造会在栈上生成对象),是不能保证只在堆上创建的,所以我们需要将构造函数与拷贝构造声明私有,因为无法通过构造函数与拷贝构造创建,所以我们需要引入一个静态成员函数,在此函数中创建对象

实现方式:
1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
class HeapOnly
{
public:static HeapOnly* GetObj()//设置个静态成员函数创建对象{return new HeapOnly;}private:HeapOnly(){}// C++98防拷贝:声明成私有//HeapOnly(const HeapOnly& );
public:// C++11 : 声明成deleteHeapOnly(const HeapOnly&) = delete;
};int x7()
{//HeapOnly hp;//HeapOnly* p = new HeapOnly;//HeapOnly* p = HeapOnly::GetObj();std::shared_ptr<HeapOnly> sp1(HeapOnly::GetObj());std::shared_ptr<HeapOnly> sp2(HeapOnly::GetObj());//HeapOnly copy(*sp1);//调用拷贝构造构造函数system("pause");return 0;
}

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

同样的,当我们了解了只能在堆上创建对象的方式之后,我们可以仿照这种方式,创建一个仅能在栈上创建对象的类

方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可。

class StackOnly
{
public:static StackOnly GetObj(){return StackOnly();}
private:StackOnly(){}
};

这样操作,就可以保证对象通过调用函数创建在栈上了

还有一种方式,就是直接禁掉new 函数,就不会在堆上创建对象了,但是这也有一个问题,就是无法避免在静态区创建的对象

// 这种方案存在一定程序缺陷,无法阻止在数据段(静态区)创建对象
class StackOnly
{
public:void* operator new(size_t size) = delete;
};int x8()
{StackOnly so;//StackOnly* p = new StackOnly;static StackOnly sso;return 0;
}

请设计一个类,不能被拷贝

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可

这其实就是将拷贝构造私有就可以了,别人也就无法进行拷贝了

C++98
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可
class CopyBan
{// ...private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};
原因:
1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};

请设计一个类,不能被继承

这个其实就是将构造函数私有,而子类若要想继承就必须要调父类的构造函数,所以无法继承

C++98方式
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};
C++11方法
fifinal关键字,fifinal修饰类,表示该类不能被继承
class A final
{// ....
};

单例模式

其实我们之前已经学过一些设计模式

迭代器模式--基于面向对象的三大特性之一,封装设计出来的,用一个迭代器封装以后,不暴露容器结构的情况下,统一的方式访问修改容器中的数据

适配器模式 -- 体现的是一种复用
 还有一些常见的设计模式如:工厂模式、装饰器模式、观察者模式、单例模式...
 一个类只能在全局(进程中)只有一个实例对象,就是单例模式
 什么场景下使用?比如一个进程中有一个内存池,进程中的多线程需要内存都要到这个内存池中取,那么这个内存池的类就可以设计单例模式。

我们先来演示一个最简单的单例模式

class Singleton
{
public:static Singleton* GetInstance(){if (_pinst == nullptr){_pinst = new Singleton;}return _pinst;}Singleton(const Singleton& s) = delete;private:Singleton(){}static Singleton* _pinst;
};Singleton* Singleton::_pinst = nullptr;

我们可以看到,我们首先将构造函数声明私有,使其只能通过静态的get函数来创建对象,其次在get函数中设置,第一次调用就创建对象,之后创建的直接返回这个指针,此时不管我们调用多少回也都是这个指针了,这便完成了我们最简单的单例模式

但其实这个单例模式是有问题的,它存在线程安全的问题,当有两个线程同时去调用get函数时,可能会出现都检测出为空,然后都创建了一个对象,在随后的调用中又会将第一个创建的覆盖掉,此时会出现内存泄漏

我们的解决方案就是加锁

class Singleton
{
public:static Singleton* GetInstance(){_mtx.lock();if (_pinst == nullptr){_pinst = new Singleton;}_mtx.unlock();return _pinst;}Singleton(const Singleton& s) = delete;private:Singleton(){}static Singleton* _pinst;static mutex _mtx;//声明静态锁
};Singleton* Singleton::_pinst = nullptr;
mutex Singleton::_mtx;//定义锁

但这样其实也并不是完美的,我们在new对象时是可能出现异常的,抛出异常导致无法解锁,所以我们还需对其进行修改

static Singleton* GetInstance(){//::Sleep(1000);  增加没加锁时出现线程不安全的条件(2个以上线程同时过了判断条件)// 双检查if (_pinst == nullptr){//_mtx.lock();unique_lock<mutex> lock(_mtx);if (_pinst == nullptr){_pinst = new Singleton;}//_mtx.unlock();}// ...return _pinst;}static void DelInstance()//这个函数也可以不加,对象在生命周期结束也会自动释放{//unique_lock<mutex> lock(_mtx);delete _pinst;_pinst = nullptr;}Singleton(const Singleton& s) = delete;private:Singleton(){}static Singleton* _pinst;static mutex _mtx;};

我们加上智能锁守卫,使其解锁一定会被执行,而我们在外部还加了一个判断,这就是为了双重保险,直接阻止多个线程进入锁中的情况,我们开始的两个进程,一个堵在了unique_lock,一个进去创建对象,当进行创建过后,我们就不需要加锁了,所以在给外层加个if,进行优化

这其实就是我们单例模式中的懒汉模式,当我们走到创建对象的时候,为空,我们才开始创建对象,第一次获取对象时,创建对象

懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

下面我们来看另一个单例模式,饿汉模式

// 饿汉模式  一开始(main函数之前)就创建对象class Singleton{public:static Singleton* GetInstance(){return &_inst;}Singleton(const Singleton&) = delete;private:Singleton(){}static Singleton _inst;};Singleton Singleton::_inst;// static对象是在main函数之前创建的,这会只有主线程,所以不存在线程安全。

我们饿汉模式,指的是在一开始就创建了对象,所以不存在线程安全问题

懒汉模式与饿汉模式的对比

1、懒汉模式需要考虑线程安全和释放的问题,实现相对更复杂,饿汉模式不存在以上问题,实现简单
 2、懒汉是一种懒加载模式需要时在初始化创建对象,不会影响程序的启动。饿汉模式则相反,程序启动阶段就创建初始化实力对象,会导致程序启动慢,影响体验。
 3、如果有多个单例类,假设有依赖关系(B依赖A),要求A单例先创建初始化,B单例再创建初始化,那么就不能饿汉,因为无法保证创建初始化顺序,这时用懒汉我们就可以手动控制。

总结一下:实际中懒汉模式还是更实用一些

C++--22.特殊类的设计相关推荐

  1. MFC 教程【14_SOCKET类的设计和实现】

    SOCKET类的设计和实现 WinSock基本知识 这里不打算系统地介绍socket或者WinSock的知识.首先介绍WinSock API函数,讲解阻塞/非阻塞的概念:然后介绍socket的使用. ...

  2. iOS简易蓝牙对战五子棋游戏设计思路之一——核心蓝牙通讯类的设计

    iOS简易蓝牙对战五子棋游戏设计思路之一--核心蓝牙通讯类的设计 一.引言 本系列博客将系统的介绍一款蓝牙对战五子棋的开发思路与过程,其中的核心部分有两个,一部分是蓝牙通讯中对战双方信息交互框架的设计 ...

  3. 基于Java多线程的打怪升级类游戏设计与开发

    摘要:本文论述了通过JAVA多线程.基于JAVA Swing的GUI图形用户界面设计.IO输入输出流.JDBC技术,实现了游戏系统的UI设计.游戏数据的存储.关卡的设置.用户头像的上传与更改.游戏数据 ...

  4. 【转】VB6.0中用户类和数据源类的设计和使用技术

    本文转自: http://www.pcworld.com.cn/99/script/9907/072601b.asp 摘要:用户自定义类(Class)是Visual Basic 6.0中实现软件重用的 ...

  5. 转:软件设计漫谈之三:30分钟掌握面向对象类的设计原则

    转自:http://blog.csdn.net/yunhua_lee/article/details/5105524 30分钟掌握面向对象类的设计原则 看过设计模式的人不少,但看过Martin的面向对 ...

  6. VS 2010 通过 Architecture创建UML类图设计

    [赛迪网报道]类是对应用领域或应用解决方案中概念的描述.类图以类为中心进行组织,类图中的其他元素或属于某个类,或与类相关联.类也是对象的集合,展示了对象的结构及与系统交互的行为,其目的是显示建模系统的 ...

  7. 一文讲透非标品的商品类目设计

    www.pmcaff.com 本文为作者 百转 于社区发布 只要做电商,商品类目设计就是绕不开的一环.好的类目设计能让供需双方更快.更好定义或找到所关心的商品,是整个电商体系的地基,是构建并链接各个模 ...

  8. java 重启线程_java 可重启线程及线程池类的设计(详解)

    了解JAVA多线程编程的人都知道,要产生一个线程有两种方法,一是类直接继承Thread类并实现其run()方法:二是类实现Runnable接口并实现其run()方法,然后新建一个以该类为构造方法参数的 ...

  9. Java黑皮书课后题第10章:*10.10(Queue类)10.6节给出一个Stock类。设计一个名为Queue的类用于存储整数。像栈一样,队列保存元素。在栈中,元素后进先出。队列中元素先进先出

    10.10(Queue类)10.6节给出一个Stock类,设计一个名为Queue的类用于存储整数 题目 程序 破题 代码 Test10.java Test10_Queue.java UML 题目 程序 ...

  10. Java黑皮书课后题第10章:10.4(MyPoint类)设计一个名为MyPoint的类,代表一个以x坐标和y坐标表示的点

    10.4(MyPoint类)设计一个名为MyPoint的类,代表一个以x坐标和y坐标表示的点 题目 程序 代码 Test4.java Test4_MyPoint.java 运行实例 UML 题目 程序 ...

最新文章

  1. 协议地址结构_通信之路——用最简洁的文字告诉你互联网协议TCP/IP
  2. Python+selenium 自动化-基本环境搭建,调用selenium库访问百度查询天气实例演示
  3. 3.Java中的关键字和注释
  4. c语言学习-输入三个数求三个数中的最大数
  5. python画猫和老鼠_观察者模式(猫与老鼠的故事!)
  6. 历时两年,微软物联网安全服务 Azure Sphere 全面上线!
  7. python exec函数和eval函数_Python exec()和eval()的使用注意事项
  8. Parallels Desktop 17 for Mac(pd虚拟机)支持Intel 和 Apple M1
  9. 《深入浅出数据分析》读书心得与笔记
  10. 微信小程序UI组件库集合(个人收藏)
  11. 2018二月安恒月赛WRITE UP
  12. 阻尼牛顿法_python
  13. 测试工程师进阶之测试用例发散思维(一)
  14. IIS 环境下 PHP无法显示错误信息------500错误
  15. 处nm是什么意思_“nm”是什么意思啊?
  16. 多邻国学英语 tips
  17. “数据”企业之命脉,守护有责。
  18. 使用EDAS投稿系统进行论文投稿时常遇到的问题及解决方法
  19. 帧率(FPS)计算的六种方法总结
  20. easyExcel设置最后一行的样式【可以拓展为每一行】

热门文章

  1. 暖通空调材料进场如何验收?
  2. user_agent
  3. 管理思维的逻辑之案例作业
  4. 单片机c语言音符发生,单片机C语言程序的设计实训100例基于8051Proteus仿真.doc
  5. pip install报python setup.py egg_info Check the logs for full command output.
  6. 【组合数学】组合恒等式 ( 八个组合恒等式回顾 | 组合恒等式 积 1 | 证明 | 使用场景 | 求组合数通用方法 )
  7. HTML5七夕情人节表白网页(全屏七夕表白页面滚动模板) HTML+CSS+JavaScript
  8. WebScraper for Mac(网站数据抓取工具)
  9. Python入门——爬取pubmed文献做分析
  10. java程序设计基础_陈国君版第五版_第四章习题