广州城乡建设网站,石家庄市工程造价信息网,河北石家庄最新消息今天,做网站买那种服务器文章目录一、设计模式概念二、设计一个不能被拷贝的类三、设计一个只能在堆上创建对象的类3.1 私有构造3.2 私有析构四、设计一个只能在栈上创建对象的类五、设计不能被继承的类六、单例模式❗️❗️6.1 饿汉模式6.2 懒汉模式6.2.1 线程安全问题6.2.2 新写法一、设计模式概念
…
文章目录一、设计模式概念二、设计一个不能被拷贝的类三、设计一个只能在堆上创建对象的类3.1 私有构造3.2 私有析构四、设计一个只能在栈上创建对象的类五、设计不能被继承的类六、单例模式❗️❗️6.1 饿汉模式6.2 懒汉模式6.2.1 线程安全问题6.2.2 新写法一、设计模式概念
设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。 使用设计模式的目的为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 根本原因是为了代码复用增加可维护性。
设计模式的例子迭代器模式
二、设计一个不能被拷贝的类
拷贝一共就只有两个场景一个是拷贝构造一个是赋值运算符重载。所以我们想要设计出一个不能被拷贝的类只需要让外部无法调用这两个函数即可。
在C98中我们的方法是将拷贝构造和赋值运算符重载只声明不定义并且将权限设置为私有。
class anti_copy
{
public:anti_copy(){}
private:anti_copy(const anti_copy ac);anti_copy operator(const anti_copy ac);
};设计原因 1️⃣ 私有如果声明成共有那么就可以在类外面实现定义。 2️⃣ 只声明不定义因为如果不声明编译器会默认生成这两个的默认成员函数。而不定义是因为该函数不会被调用就不用写了这样编译的时候就会出现链接错误。
而在C11中引入了关键字——delete。 如果在默认成员函数后跟上delete表示让编译器删除掉该默认成员函数。即使权限是共有也无法调用已删除的函数。
class anti_copy
{
public:anti_copy(){}anti_copy(const anti_copy ac) delete;anti_copy operator(const anti_copy ac) delete;
private:
};三、设计一个只能在堆上创建对象的类
3.1 私有构造
首先要把构造函数给私有不然这个类就可以在任意位置被创建。而构造函数被私有了以后我们怎么创建对象呢 我们可以在定义一个成员函数让这个函数在堆上申请空间但我们知道必须现有对象才能调用成员函数。所以我们就把这个函数设置成静态成员函数。
class OnlyHeap
{
public:static OnlyHeap* GetObj(){return new OnlyHeap;}
private:OnlyHeap(){}
};但是这样也不完全对如果我们这么写
class OnlyHeap
{
public:static OnlyHeap* GetObj(){return new OnlyHeap;}
private:OnlyHeap(){}
};int main()
{OnlyHeap* hp1 OnlyHeap::GetObj();OnlyHeap hp2(*hp1);return 0;
}这里的hp2就是栈上的对象。所以我们也要把拷贝构造给封住。
class OnlyHeap
{
public:static OnlyHeap* GetObj(){return new OnlyHeap;}OnlyHeap(const OnlyHeap hp) delete;
private:OnlyHeap(){}
};3.2 私有析构
class OnlyHeap
{
public:OnlyHeap(){}OnlyHeap(const OnlyHeap hp) delete;
private:~OnlyHeap(){}
};int main()
{OnlyHeap hp1;// errorOnlyHeap* hp2 new OnlyHeap;return 0;
}这里的hp1就不能创建成功因为对象销毁的时候会调用析构函数但是这里的析构是私有的所以该对象无法调用。
但是我们要销毁hp2该怎么办呢 我们可以定义一个成员函数显示调用析构函数。
class OnlyHeap
{
public:OnlyHeap(){}OnlyHeap(const OnlyHeap hp) delete;void Destroy(){this-~OnlyHeap();}
private:~OnlyHeap(){}
};int main()
{OnlyHeap* hp2 new OnlyHeap;hp2-Destroy();return 0;
}四、设计一个只能在栈上创建对象的类
为了不让这个类随便定义出对象首先要把构造函数私有。然后跟上面只能在堆上创建对象的方法相似定义出一个静态成员函数返回栈上创建的对象。
class StackOnly
{
public:static StackOnly GetObj(){return StackOnly();}
private:StackOnly(){}
};int main()
{StackOnly hp StackOnly::GetObj();return 0;
}但是这里有一个问题无法防止创建静态对象
static StackOnly hp2 StackOnly::GetObj();五、设计不能被继承的类
在C98为了不让子类继承我们可以把构造函数私有化因为子类需要先调用父类的构造函数初始化父类的那一部分成员。
class NoInherit
{
public:
private:NoInherit(){}
};而在C11中引入的新的关键字final被final关键字修饰的类不能被继承。
class NoInherit final
{
public:
private:
};六、单例模式❗️❗️
一个类只能创建一个对象即单例模式该模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。
单例模式的特点就是全局只有一个唯一对象。
6.1 饿汉模式
怎么能做到全局只是用一个对象呢比方说我们现在想要实现一个英汉字典首先我们要把构造函数私有不然无法阻止创建对象。然后我们可以在类里面定义一个自己类型的静态成员变量作用域是全局的。因为对比定义在外边的静态成员变量内部的可以调用构造函数。 这里要注意把拷贝也要封住。
class Singleton
{
public:static Singleton GetObj(){return _s;}void insert(const std::string s1, const std::string s2){_dict[s1] s2;}void Print(){for (auto e : _dict){cout e.first - e.second endl;}}// 防拷贝Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;
private:Singleton(){}std::mapstd::string, std::string _dict;
private:static Singleton _s;// 声明
};Singleton Singleton::_s;// 定义int main()
{Singleton::GetObj().insert(corn, 玉米);Singleton dic1 Singleton::GetObj();dic1.insert(apple, 苹果);dic1.insert(banana, 香蕉);Singleton dic2 Singleton::GetObj();dic2.insert(pear, 梨);dic2.Print();return 0;
}饿汉模式有什么特点呢 它会在一开始main之前就创建对象。 饿汉模式有什么缺点呢 1️⃣ 如果单例对象构造十分耗时或者占用很多资源比如加载插件啊 初始化网络连接啊读取文件啊等等而有可能该对象程序运行时不会用到那么也要在程序一开始就进行初始化就会导致程序启动时非常的缓慢。 2️⃣ 多个单例类之间如果有依赖关系饿汉模式就无法控制比方说要求A类初始化时必须调用B但是饿汉无法控制先后顺序。 所以针对这些问题就有了懒汉模式。
6.2 懒汉模式
第一次使用实例对象时创建对象用的时候创建。进程启动无负载。多个单例实例启动顺序自由控制。
我们可以直接对上面饿汉模式的代码进行修改把静态成员变量变成指针。然后把获取的函数改变一下
static Singleton GetObj(){// 第一次调用才会创建对象if (_s nullptr){_s new Singleton;}return *_s;}整体代码
class Singleton
{
public:static Singleton GetObj(){// 第一次调用才会创建对象if (_s nullptr){_s new Singleton;}return *_s;}void insert(const std::string s1, const std::string s2){_dict[s1] s2;}void Print(){for (auto e : _dict){cout e.first - e.second endl;}}// 防拷贝Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;
private:Singleton(){}std::mapstd::string, std::string _dict;
private:static Singleton* _s;// 声明
};Singleton* Singleton::_s nullptr;// 定义6.2.1 线程安全问题
上面的代码存在问题当多个线程同时调用GetObj()就会创建多个对象。所以为了线程安全我们要加锁。为了保证锁自动销毁我们可以自定义一个锁。
template class Lock
class LockAuto
{
public:LockAuto(Lock lk): _lk(lk){_lk.lock();}~LockAuto(){_lk.unlock();}
private:Lock _lk;
};class Singleton
{
public:static Singleton GetObj(){// 第一次调用才会创建对象if (_s nullptr)// 只有第一次才用加锁{LockAutomutex lock(_mutex);if (_s nullptr){_s new Singleton;}}return *_s;}void insert(const std::string s1, const std::string s2){_dict[s1] s2;}void Print(){for (auto e : _dict){cout e.first - e.second endl;}}// 防拷贝Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;
private:Singleton(){}std::mapstd::string, std::string _dict;
private:static Singleton* _s;// 声明static mutex _mutex;// 锁
};Singleton* Singleton::_s nullptr;// 定义
mutex Singleton::_mutex;// 定义6.2.2 新写法
class Singleton
{
public:static Singleton GetObj(){static Singleton dic;return dic;}void insert(const std::string s1, const std::string s2){_dict[s1] s2;}void Print(){for (auto e : _dict){cout e.first - e.second endl;}}// 防拷贝Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;
private:Singleton(){}std::mapstd::string, std::string _dict;
};这里就用了静态局部变量只会在第一次定义的时候初始化。在C11之前是不能保证线程安全的但是C11之后就可以了。