<剑指offer>深度探索(一)实现string 类以及单例模式的具体实现

来源:互联网 发布:武汉学编程学校 编辑:程序博客网 时间:2024/06/02 01:02

面试题(一)

实现string 类

#include<iostream>using namespace std;class String{public:String(const char *str = NULL);String(const String  &other);String& operator=(const String  &other);~String();private:char *m_data;};String::~String(){delete[]m_data;}String::String(const char *str){if(str == NULL){m_data = new char[1];*m_data = '\0';}else{int length = strlen(str);m_data = new char[length+1];strcpy(m_data, str);}}String::String(const String &other){int length = strlen(other.m_data);m_data = new char[length+1];strcpy(m_data, other.m_data);}String& String::operator=(const String &other){if(this == &other)return *this;delete []m_data;int length = strlen(other.m_data);m_data = new char[length+1];strcpy(m_data, other.m_data);return *this;}int main(){String s1("abc");String s2 = s1;String s3(s1);String s4;s4 = s3;}
面试题(二)

实现单例模式

(一)概念

可以控制类的实例的生成个数,即单例模式可以保证系统中一个类只有一个实例

(二)特点

(1)某个类只能有一个实例,提供一个访问它的全局访问点,该实例被所有程序模块共享

(2)它必须自行创建这个实例

(3)必须自行向整个系统提供这个实例

(三)具体体现

(1)单例模式中的类只提供私有的构造函数

(2)定义了一个单例类,使用类的私有静态指针变量指向类的唯一实例

(3)提供了一个公有的静态方法来获取该实例

单例模式通过类本身来管理其唯一实例,这种特性提供了解决问题的方法,唯一的实例是该类的一个普通对象,但是设计这个类时,让它只能创建

一个实例并提供对此实例的全局访问,singleton 在静态成员函数中隐藏创建实例的操作,习惯上,我们把这个成员叫做Instance(),返回值是唯一实例的指针

(4)三种模式:懒汉式, 饿汉式, 多线程式

(一)懒汉式

#include<iostream>using namespace std;class mysingleton{private:mysingleton(){}private:static mysingleton *m_pInstance;public:static mysingleton* GetInstance(){if(m_pInstance == NULL)m_pInstance = new mysingleton();return m_pInstance;}};mysingleton *mysingleton::m_pInstance = NULL;ˉint main(){mysingleton *p1 = mysingleton::GetInstance();mysingleton *p2 = mysingleton::GetInstance();cout<<p1<<endl;cout<<p2<<endl;}

分析特点:延迟加载,缺点是没有释放内存,导致内存泄漏


(二)饿汉式

class mysingleton{private:mysingleton(){}public:static mysingleton *GetInstance(){static mysingleton instance;return &instance;}};


分析特点:一开始就加载了,是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变

“懒汉式“用时间交换空间,不是线程安全的

饿汉式“用空间交换时间,是线程安全的

关于线程安全的概念:

不管多个线程是怎样的执行顺序和优先级,或是wait, sleep, join 等控制方式,如果一个类在多线程的访问下运转一切正常,并且访问类不需要进行额外的同步处理或者协调,就认为它是线程安全的


(三)多线程式(加锁)

class mysingleton{private:mysingleton(){}private:static mysingleton *m_pInstance;public:static mysingleton* GetInstance(){if(NULL = m_pInstance){Lock();if(NULL == m_pInstance){  m_pInstance = new mysingleton();  return m_pInstance;}Unlock();}}};

此处进行了两次m_Instance == NULL的判断,借鉴了JAVA 的单例模式实现时,使用的所谓的“双检锁”机制,因为进行一次加锁和解锁是需要付出对应代价的,而进行两次判断,就可以避免多次加锁和解锁操作,同时也保证了线程安全

但是,如果进行大量的数据操作,加锁操作会成为一个性能的瓶颈


下面是改进方法,针对大量数据操作

#include<iostream>using namespace std;class mysingleton{public:static mysingleton *GetInstance(){return const_cast<mysingleton*>(m_pInstance);//关于显示转换,我接下来会进行系统的总结}static void DestroyInstance(){if(m_pInstance != NULL){delete m_pInstance;m_pInstance = NULL;cout<<"析构"<<endl;}}private:mysingleton(){}static const mysingleton *m_pInstance;}; const mysingleton *mysingleton::m_pInstance = new mysingleton();int main(){mysingleton *p1 = mysingleton::GetInstance();mysingleton *p2 = mysingleton::GetInstance();cout<<p1->GetInstance()<<endl;cout<<p2->GetInstance()<<endl;    mysingleton::DestroyInstance();}
分析:因为静态初始化在程序开始之前,也就是进入主函数之前,由主线程以单线程的方式完成了初始化,所以静态初始化实例保证了线程安全性,在性能比较高时,就可以使用这种方式,从而避免频繁的加锁和解锁的资源浪费


以下是解决内存泄漏的方法

(1)

m_pInstance 指向的空间什么时候释放,这个实例的析构操作是什么时候执行?

解决:让类在自己合适的时候把它删除

程序在结束的时候,系统会自动析构所有的全局变量,事实上,系统也会析构掉所有类的静态成员变量,就像这些静态成员变量也是全局变量一样,利用这个特征,我们可以在C++单例模式类中定义一个这样的静态成员变量,唯一的工作就是在析构函数中删除单例类的实例

class mysingleton{public:static mysingleton *GetInstance(){if(m_pInstance == NULL){m_pInstance = new mysingleton();return m_pInstance;}}class CGarbo{public:~CGarbo(){if(mysingleton::m_pInstance){delete mysingleton::m_pInstance;}}};static CGarbo Garbo;private:mysingleton(){}static mysingleton *m_pInstance;};
分析:

类Garbo 被定义为mysingleton 的私有内嵌类,以防该类被在其他地方滥用

在程序运行结束时,系统会调用mysingleton 的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例

这种方法的特点:

(1)在单例类的内部定义专有的嵌套类

(2)在单例类内定义私有的专门用于释放的静态成员

(3)利用程序在结束时析构全局变量的特性,选择最终的释放时机

在程序结束的时候,系统会析构所有的全局变量,实际上,系统也会析构所有类的静态变量,就像这些静态变量是全局变量一样,静态变量和全局变量都是存储在静态存储区的,所以在析构时,是同等对待的


(2)#include<iostream>using namespace std;class mysingleton{public:static mysingleton *GetInstance(){if(m_pInstance == NULL){m_pInstance = new mysingleton();return m_pInstance;}}static void DestroyInstance(){if(m_pInstance != NULL){delete m_pInstance;m_pInstance = NULL;cout<<"析构"<<endl;}}private:mysingleton(){}static mysingleton *m_pInstance;};mysingleton *mysingleton::m_pInstance = NULL;int main(){mysingleton *p1 = mysingleton::GetInstance();mysingleton *p2 = mysingleton::GetInstance();cout<<p1->GetInstance()<<endl;cout<<p2->GetInstance()<<endl;mysingleton::DestroyInstance();}

常用场景:

单例模式常常与工厂模式相结合使用,因为工厂只需要创建产品实例就可以了,在多线程的环境下也不会有任何冲突,因此只需要有一个产品实例就可以了