个人学习笔记
之前的介绍过关于单例模式的例子,其中对象是由_pInstance指针来保存,通过new创建的对象并没有进行释放,是因为单例模式之后没有其他代码需要执行,程序会立马结束,操作系统会自行回收相关资源,但对于后面有相关代码需要执行的程序来说,这种操作就会造成内存泄漏。有没有什么方式可以让对象自动释放?这样既可以避免内存泄漏,又可以让程序员不用关注对象释放的问题。在涉及到自动的问题时,我们很自然的可以想到:对象被销毁时,会自动调用其析构函数。利用这一特性,我们可以解决这一问题,这里介绍4种方式对单例模式进行自动释放:友元形式、内部类加静态数据成员形式、atexit形式、pthread_once形式。
创建一个新类AutoRelease,作为Singleton的友元,创建该类的栈对象,由于编译器会自动释放栈对象,在释放栈对象时会执行析构函数,这样执行析构函数的同时顺便把单例模式的对象给释放了,这样就可以达到目的了。
代码如下(示例):
#include <iostream>
using std::cout;
using std::endl;
class AutoRelease;
//1、友元形式
class Singleton
{
friend AutoRelease;
public:
static Singleton * getInstance()
{
if(nullptr == _pInstance)
{
_pInstance = new Singleton();
}
return _pInstance;
}
//不需要主动调用来释放对象了
static void destroy()
{
if(_pInstance)
{
cout << "destroy()" << endl;
delete _pInstance;
_pInstance = nullptr;
}
}
private:
Singleton()
{
cout << "Singleton()" << endl;
}
~Singleton()
{
cout << "~Singleton()" << endl;
}
private:
static Singleton * _pInstance;
};
class AutoRelease
{
public:
AutoRelease()
{
cout << "AutoRelease()" << endl;
}
~AutoRelease()
{
cout << "~AutoRelease()" << endl;
if(Singleton::_pInstance)//作为友元类,静态数据成员可以通过类名加作用域限定符获取
{
delete Singleton::_pInstance;
Singleton::_pInstance = nullptr;
}
}
};
Singleton *Singleton::_pInstance = nullptr;
int main()
{
Singleton *s1 = Singleton::getInstance();
Singleton *s2 = Singleton::getInstance();
cout << "&s1 = " << s1 << endl;
cout << "&s2 = " << s2 << endl;
/* s1->destroy(); */
/* s2->destroy(); */
/* Singleton::destroy(); */
AutoRelease ar;//创建栈对象,栈对象销毁的同时,顺便把Singeleton对象进行销毁,不用调用destroy函数,自动把单例对象释放
return 0;
}
除了友元,还可以把AutoRelease作为Singleton的内部类,用public修饰时,可以直接在外面创建AutoRelease的栈对象,其作用原理与友元形式类似,因为AutoRelease只为Singleton服务,所以AutoRelease要用private修饰,用private修饰后,AutoRelease对象就不能在Singleton类外面创建,所以只能在Singleton类里面创建,作为Singleton的普通数据成员,在Singleton里创建AutoRelease对象时,会调用AutoRelease构造函数,而此时的AutoRelease对象并不是栈对象,所以不会自动执行析构函数,需要Singleton创建的类对象销毁时才会回收数据成员,这时才会执行AutoRelease的析构函数,进而再回收Singleton的对象,而Singleton创建的单例对象就是靠AutoRelease才能释放掉,这里就陷入了一个死循环,所以就不能创建AutoRelease对象作为Singleton的普通数据成员,也就是说Singleton对象不能拥有AutoRelease对象作为普通数据成员,所以需要把AutoRelease创建的对象从Singleton创建的对象中拿出来,放到全局/静态区的位置,也就是让AutoRelease创建的对象作为Singleton的静态数据成员,不仅仅被某个Singleton对象拥有,它是一个全局的,不受某一个Singleton对象控制,又因为处于全局/静态区的对象或变量会随着进程的结束而进行销毁,这时就会执行AutoRelease的析构函数,进而把_pInstance给delete掉,这时就会执行Singleton的析构函数,销毁释放Singleton单例模式创建的对象,最终达到目的。
代码如下(示例):
#include <iostream>
using std::cout;
using std::endl;
class AutoRelease;
//2、内部类 + 静态数据成员
class Singleton
{
public:
static Singleton * getInstance()
{
if(nullptr == _pInstance)
{
_pInstance = new Singleton();
}
return _pInstance;
}
static void destroy()
{
if(_pInstance)
{
cout << "destroy()" << endl;
delete _pInstance;
_pInstance = nullptr;
}
}
private:
class AutoRelease
{
public:
AutoRelease()
{
cout << "AutoRelease()" << endl;
}
~AutoRelease()
{
cout << "~AutoRelease()" << endl;
if(_pInstance)//内部类可以直接使用_pInstance
{
delete _pInstance;
_pInstance = nullptr;
}
}
};
private:
Singleton()
{
cout << "Singleton()" << endl;
}
~Singleton()
{
cout << "~Singleton()" << endl;
}
private:
static Singleton * _pInstance;
static AutoRelease _ar;//不是栈对象
};
Singleton *Singleton::_pInstance = nullptr;
Singleton::AutoRelease Singleton::_ar;//放到全局静态的位置,编译器会自动赋一个初值
int main()
{
Singleton *s1 = Singleton::getInstance();
Singleton *s2 = Singleton::getInstance();
cout << "&s1 = " << s1 << endl;
cout << "&s2 = " << s2 << endl;
/* Singleton::AutoRelease ar; */
/* s1->destroy(); */
/* s2->destroy(); */
/* Singleton::destroy(); */
cout << sizeof(Singleton) << endl;//=1
return 0;
}
这里需要借助一个C的函数atexit(),atexit函数会注册一个函数,注册的函数会在进程正常结束的时候调用,通过atexit注册多次就会调用多次。这时需要考虑atexit的位置,考虑到Singleton单例模式创建的对象只能被销毁一次,所以atexit()最好是放到单例模式创建的作用域内。但是在多线程环境下,上面的代码都是有缺陷的,因为if(nullptr==_pInstance)判断语句对于有几个线程的情况下,可能会同时满足if判断语句中的内容,然后创建多个Singleton对象,这样不仅不符合单例模式(一个类有且只能有一个对象),而且释放的时候不会把多线程创建的多个对象全部销毁释放掉,从而造成内存泄漏,这种情况下,对于多线程环境下是不安全的。出现这种情况是因为,在进入if判断语句之前,_pInstance的值为空,所以这里我们可以在_pInstance初始化的时候赋一个值,这样下次进入getInstance()时,_pInstance已经有值了,即使在多线程环境下,也不会创建多个对象,这样解决了多线程环境下不安全的问题,这就对应一种模式:饿汉模式。
代码如下(示例):
#include <stdlib.h>
#include <iostream>
using std::cout;
using std::endl;
class AutoRelease;
//3、atexit + 饿汉模式
//可以解决多线程环境下不安全的问题
class Singleton
{
public:
static Singleton * getInstance()
{
if(nullptr == _pInstance)
{
_pInstance = new Singleton();
atexit(destroy);
}
return _pInstance;
}
static void destroy()
{
cout << "destroy()" << endl;
if(_pInstance)
{
delete _pInstance;
_pInstance = nullptr;
}
}
private:
Singleton()
{
cout << "Singleton()" << endl;
}
~Singleton()
{
cout << "~Singleton()" << endl;
}
private:
static Singleton * _pInstance;
};
/* Singleton *Singleton::_pInstance = nullptr;//饱汉模式(懒汉模式) */
Singleton *Singleton::_pInstance = getInstance();//饿汉模式
int main()
{
Singleton *s1 = Singleton::getInstance();
Singleton *s2 = Singleton::getInstance();
cout << "&s1 = " << s1 << endl;
cout << "&s2 = " << s2 << endl;
return 0;
}
其实在linux系统中,有一个函数可以保证多线程环境下安全性的问题,它就是pthread_once(),完整形式:int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)),该函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。在多线程编程环境下,尽管pthread_once()调用会出现在多个线程中,但init_routine()函数仅执行一次,究竟在哪个线程中执行是不定的,是由内核调度来决定。所以在这种情况下,即使在多线程环境下,选择饱汉模式或者饿汉模式都都可以,因为init_routine()函数只会执行一次。注意一点该形式只能在linux系统下使用,跨平台性不高。在linux系统通过g++编译的时候需要加上线程库-lpthread,其中这个p是POSIX,它是linux下的一个标准。
代码如下(示例):
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
using std::cout;
using std::endl;
class AutoRelease;
//4、pthread_once形式
//可以解决多线程环境下不安全的问题
class Singleton
{
public:
static Singleton * getInstance()
{
//getInstance为静态函数,所以_once、init也必须设置为静态,否则在getInstance中不能访问
pthread_once(&_once, init);//_once一旦初始化后,即使在多线程环境下,init()函数仅执行一次
return _pInstance;
}
static void init()
{
_pInstance = new Singleton();
atexit(destroy);
}
static void destroy()
{
cout << "destroy()" << endl;
if(_pInstance)
{
delete _pInstance;
_pInstance = nullptr;
}
}
private:
Singleton()
{
cout << "Singleton()" << endl;
}
~Singleton()
{
cout << "~Singleton()" << endl;
}
private:
static Singleton * _pInstance;
static pthread_once_t _once;
};
Singleton *Singleton::_pInstance = nullptr;//饱汉模式(懒汉模式)
/* Singleton *Singleton::_pInstance = getInstance();//饿汉模式 */
pthread_once_t Singleton::_once = PTHREAD_ONCE_INIT;
int main()
{
Singleton *s1 = Singleton::getInstance();
Singleton *s2 = Singleton::getInstance();
cout << "&s1 = " << s1 << endl;
cout << "&s2 = " << s2 << endl;
cout << PTHREAD_ONCE_INIT << endl;//=0
return 0;
}
文章浏览阅读5.9k次。今天偶尔在网上看到这边文章 http://hold-on.iteye.com/blog/9914031、我们都知道,在Android的开发中,非UI线程不能操作UI线程中的控件,即UI是非线程安全的;2、但是在工作线程(非UI线程)中调用ProgressBar控件的setProgress(int count)方法时,程序运行并 不会出现异常,3、但是如调用setBackGround(int_android的ui控件是安全的吗
文章浏览阅读109次。带有yield的函数不仅仅只用于for循环中,而且可用于某个函数的参数,只要这个函数的参数允许迭代参数。比如array.extend函数,它的原型是array.extend(iterable)。带有 yield 的函数不再是一个普通函数,而是一个生成器generator,可用于迭代,工作原理同上。yield 是一个类似 return 的关键字,迭代一次遇到yield时就返回y..._py yeild
文章浏览阅读1.4k次。最近做项目,遇到了LaunchScreen欢迎页图片加载不上或者说不显示的问题。写个笔记记录一下。。。一开始一切都是顺利的,也可以显示欢迎页的图片,但是后来做别的功能时,图片突然不显示,试了好多方法,重新写LaunchScreen,或者换别的图片,但是都不显示,最后在网上看到别人说,是xcode的问题,我们的资源没有加载上,然后我就把测试机重启了,xcode也重启了,问题就解决了。_launchscreen 偶尔为大黑板
文章浏览阅读405次。1、Qt框架图:2、Qt模块组成通用软件开发模块QtCore核心非图形接口类,为其他模块所调用QtGuiGUI(图形用户接口)功能模块QtMultimedia提供低级多媒体功能支持的类QtNetwork提供对网..._qt体系结构图
文章浏览阅读1.9k次,点赞2次,收藏20次。前言本文对语义分割相关重要论文进行了简要概述,介绍了它们的主要改进方法和改进效果,并提供了这些论文的下载方式。本文来自公众号CV技术指南的技术总结系列点个关注 ,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读。语义分割 (Semantic segmentation) 是指将图像中的每个像素链接到类标签的过程。这些标签可能包括人、车、花、家具等。我们可以将语义分割视为像素级别的图像分类。例如,在有很多汽车的图像中,分割会将所有对象标记为汽车对象。然而,称为实例分割 (insta_语义分割后显示文字
文章浏览阅读869次。http://blog.csdn.net/zhaoqiliang527/article/details/4095873_web 条码 打印
文章浏览阅读484次。TEMPORARY TABLE ProblemsThe following list indicates limitations on the use of TEMPORARY tables: A TEMPORARY table can only be of type MEMORY, ISAM, MyISAM, MERGE, or InnoDB. Temporary tables are not ..._mysql临时表自关联
文章浏览阅读8.7k次,点赞2次,收藏5次。题目:类Test1、类Test2定义如下:public class Test1 {public float aMethod(float a, float b) throws IOException { }}public class Test2 extends Test1{}将以下哪种方法插入行6是不合法的。()A、float aMethod(float a,float b){}B、public int aMethod(int a,int b) throws Excepti_类test1、test2定义如下: 1. public class test1 2.{ public float amethod(float
文章浏览阅读1.5w次,点赞8次,收藏40次。由于UDP是面向无连接的通信,所以实际上服务端和客户端是对等的,只不过服务端绑定了一个监听端口,而客户端每次都是自动分配的连接端口,其实也可以做成两边都绑定端口或者都不绑定端口的P2P模式。以下UDP是同步模式。建立两个unity工程,编写脚本,挂到场景中。服务端using UnityEngine;using System.Collections;//引入库u_endpoint clientend
文章浏览阅读2.2k次。这里很好的一点就是给出了标定结果的各个参数的含义,这个很多都没讲转载自:https://blog.csdn.net/artista/article/details/51125560ROS摄像机的标定ArtistA 2016-04-11 21:06:07 8418 收藏 8 分类专栏: ROS本文主要为ROS camera_calibration 单目相机标定教程的翻译原文:http://wiki.ros.org/camera_calibration/Tutorials/Monocul_distortion_coefficients
文章浏览阅读700次。HTML基础总结_在html语言中,设置编号列表后整个项目列表包含在 和 标签之间,列表的每一项包
文章浏览阅读210次。一、通过tartall归档文件安装(一)、基本步骤取得源文件:将tartall文件在/usr/local/src目录下解压缩;取得步骤流程:进入新建的目录下,区查阅INSTALL和README等相关文件;相关属性软件安装:根据INSTALL、README的内容说明安装好一些相关的软件(非必须);建立makefile:以自动检测程序(configure或autoconfig)检测环境变量..._this install program copies files (often just compiled) into destination loc