提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!
提示:以下是本篇文章正文内容,下面案例可供参考
拷贝只会发生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝, 只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
- class CopyBan
- {
- // ...
-
- private:
- CopyBan(const CopyBan&);
- CopyBan& operator=(const CopyBan&);
- //...
- };
原因:
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上 =delete,表示让编译器删除掉该默认成员函数。
- class CopyBan
- {
- // ...
- CopyBan(const CopyBan&) = delete;
- CopyBan& operator=(const CopyBan&) = delete;
- //...
- };
实现方式:
- // 对象只能在堆上
- class HeapOnly
- {
- public:
- template<class... Args>
- static HeapOnly* CreateObj(Args&&... args)
- {
- // 访问限定符只限定类外面的,而不限定类里面的,可以在类里面创建一个该类型的对象
- return new HeapOnly(args...);
- }
-
- HeapOnly(const HeapOnly&) = delete;
- HeapOnly& operator=(const HeapOnly&) = delete;
- private:
- // 构造私有化
- HeapOnly()
- {}
- // 带参的构造函数
- HeapOnly(int x, int y)
- :_x(x)
- ,_y(y)
- {}
-
- int _x;
- int _y;
- vector<int> _a;
- };
-
- int main()
- {
- //HeapOnly ho1;
- //HeapOnly* ho2 = new HeapOnly;
-
- // 我们想要调用类成员函数,我们就得有类的对象,但是类的对象又是由该函数生成的,这就是先有鸡还是先有蛋的问题;
- // 所以我们可以将生成类的对象的函数定义为静态的
- HeapOnly* ho3 = HeapOnly::CreateObj();
- HeapOnly* ho4 = HeapOnly::CreateObj(1,1);
-
- //HeapOnly copy(*ho3);
-
- return 0;
- }
- // 只能在堆上(方法二)
- class HeapOnly
- {
- public:
- // 构造私有化
- HeapOnly()
- {}
-
- HeapOnly(int x, int y)
- :_x(x)
- ,_y(y)
- {}
-
- void Destroy()
- {
- delete this;// 调用析构函数
- // delete分为两步:先调用析构,再free指向的空间
- }
-
- private:
- // 封掉析构函数,但是还要用析构,所以不能彻底封死,放到私有部分
- ~HeapOnly()
- {
- cout << "~HeapOnly()" << endl;
- }
-
- int _x;
- int _y;
- vector<int> _a;
- };
-
- int main()
- {
-
- //HeapOnly ho1;// 栈区对象出了作用域,会自动调用析构函数
- // 方法一:手动释放空间
- HeapOnly* ptr = new HeapOnly;// 不会自动调用析构,因为是堆区上面的
-
- //delete ptr;
- ptr->Destroy();
-
- // 方法二:lambda表达式
- // 释放的删除器是delete,但是底层的delete调不动,会报错,所以只能默认的写一个删除器,调用Destroy()函数
- shared_ptr
ptr(new HeapOnly, [](HeapOnly* ptr) {ptr->Destroy(); }) ; -
- return 0;
- }
将构造函数私有化,阻止了在堆上创建对象,然后设计静态方法创建对象返回即可。
new是由operator new和构造函数(拷贝构造也是构造)两部分组成,封一下operator new,operator new是默认调用的是全局的operator new,我们可以重载一个专属的operator new
- // 对象只能在Stack
- class StackOnly
- {
- public:
- template<class... Args>
- static StackOnly CreateObj(Args&&... args)
- {
- return StackOnly(args...);// 返回一个栈上的匿名对象
- }
-
- //StackOnly(const StackOnly&) = delete;
- StackOnly& operator=(const StackOnly&) = delete;
-
- // 重载一个类专属的operator new
- // 意思是new想在堆区上开辟一个StackOnly类型的对象,就不能调用全局的operator new,只能调用该类的operator new
- // 但是该类的operator new()封住了,所以new也就不能在堆区上开辟空间了
- void* operator new(size_t n) = delete;
- void operator delete(void* p) = delete; // 禁止调用delet
-
- private:
- // 构造私有化,阻止了在堆上创建对象
- StackOnly()
- {}
-
- StackOnly(int x, int y)
- :_x(x)
- ,_y(y)
- {}
-
- int _x;
- int _y;
- vector<int> _a;
- };
-
- int main()
- {
- // 优化了之后,可能是直接构造,但是优化是在语法逻辑之后,得先拷贝,然后再优化
- StackOnly so1 = StackOnly::CreateObj();
- StackOnly so2 = StackOnly::CreateObj(1,1);
- // 因为上面两行,拷贝构造函数是不能封死的
-
- // new的时候,调不了构造函数,但可以调用拷贝构造函数(没封)
- // new是在堆区上开辟一个对象空间,我们只想在栈区上开辟空间,但是拷贝构造又不能封死,所以要让new不能使用
- //StackOnly* so3 = new StackOnly(so1);
-
- // new是由operator new和构造函数(拷贝构造也是构造)两部分组成
- // 封一下operator new,operator new是默认调用的是全局的operator new,我们可以重载一个专属的operator new
-
- return 0;
- }
- // C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
- class NonInherit
- {
- public:
- static NonInherit GetInstance()
- {
- return NonInherit();
- }
- private:
- NonInherit()
- {}
- };
final关键字,final修饰类,表示该类不能被继承。
- class A final
- {
- // ....
- };
设计模式:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模 式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个 访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置 信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再 通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式:
就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
- namespace hunger
- {
- // 饿汉:一开始(main之前)就创建出全局的对象
- // 问题:
- // 1、如果单例对象数据较多,构造初始化成本较高,那么会影响程序启动的速度。迟迟进不了main函数
- // 2、多个单例类有初始化启动依赖关系,饿汉无法控制。
- // 假设:A和B两个单例,假设要求A先初始化,B再初始化,饿汉无法保证。
- // 都是全局的,谁先谁后不清楚,而且还定义在不同的文件
- class Singleton
- {
- public:
- static Singleton* GetInstance()
- {
- return &_sint;// 返回该类型的对象
- }
-
- void Print()
- {
- cout << _x << endl;
- cout << _y << endl;
-
- for (auto& e : _vstr)
- {
- cout << e << " ";
- }
- cout << endl;
- }
- // 添加数据
- void AddStr(const string& s)
- {
- _vstr.push_back(s);
- }
-
- Singleton(Singleton const&) = delete;
- Singleton& operator=(Singleton const&) = delete;
-
- private:
- // 构造函数私有化
- Singleton(int x = 0, int y = 0, const vector
& vstr = { "yyyyy","xxxx" }) - :_x(x)
- , _y(y)
- , _vstr(vstr)
- {}
-
- // 想让一些数据,当前程序只有一份,那就可以把这些数据放到这个类里面
- // 再把这个类设计成单例,这个数据就只有一份了
- int _x;
- int _y;
- vector
_vstr; -
- // 静态成员对象,不存在类的其中一个对象中,存在静态区,相当于全局的,是属于这个类中的所有对象的,
- // 定义在类中,受类域限制,但是能访问私有的构造函数
- static Singleton _sint;
- };
-
- // 静态成员变量在类里面声明,在类外面定义;也可以调用私有的构造函数
- // 第一个Singleton是类型;第二个Singleton是类域
- Singleton Singleton::_sint(1, 1, { "陕西","四川" });
- }
-
- // Singleton _sint;
- // 在类外面不能创建该类的对象,因为该类的构造函数私有话了,所以只能把它放到类里面,写成静态的
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避 免资源竞争,提高响应速度更好。
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化, 就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
- // 完美的解决了饿汉的问题(懒汉模式)
- namespace lazy
- {
- // 懒汉方式一:两个问题:new出来的对象要考虑释放问题、线程安全问题
- class Singleton
- {
- public:
- static Singleton* GetInstance()
- {
- // 第一次调用时,创建单例对象
- // 线程安全问题,需要加锁
- if (_psint == nullptr)
- {
- _psint = new Singleton;
- }
-
- return _psint;
- }
-
- static void DelInstance()
- {
- if (_psint)
- {
- delete _psint;
- _psint = nullptr;
- }
- }
-
- void Print()
- {
- cout << _x << endl;
- cout << _y << endl;
-
- for (auto& e : _vstr)
- {
- cout << e << " ";
- }
- cout << endl;
- }
-
- void AddStr(const string& s)
- {
- _vstr.push_back(s);
- }
-
- Singleton(Singleton const&) = delete;
- Singleton& operator=(Singleton const&) = delete;
-
- private:
- Singleton(int x = 0, int y = 0, const vector
& vstr = { "yyyyy","xxxx" }) - :_x(x)
- , _y(y)
- , _vstr(vstr)
- {}
-
- // 什么情况下需要显示的释放?
- ~Singleton()
- {
- // 把数据写到文件
- cout << "~Singleton()" << endl;
- }
-
- // 想让一些数据,当前程序只有一份,那就可以把这些数据放到这个类里面
- // 再把这个类设计成单例,这个数据就只有一份了
- int _x;
- int _y;
- vector
_vstr; -
- // 静态成员对象,不存在对象中,存在静态区,相当于全局的,定义在类中,受类域限制
- // 懒汉模式 ---> 第一种方式:使用指针
- static Singleton* _psint;
-
- // 万一外面忘记调用DelInstance()函数来显示释放单例对象,怎么办?
- // 内部类
- class GC
- {
- public:
- ~GC()
- {
- Singleton::DelInstance();
- }
- };
- static GC gc;// 定义一个全局的GC类的对象,出了作用域会自动调用析构函数
- };
-
- Singleton* Singleton::_psint = nullptr;
- Singleton::GC Singleton::gc;
- // 懒汉方式二:
- class Singleton
- {
- public:
- static Singleton* GetInstance()
- {
- // 局部的静态对象,第一次调用函数时构造初始化;第二次调用函数时,不会重复构造初始化
- // C++11及之后这样写才可以
- // C++11之前无法保证这里的构造初始化是线程安全
- static Singleton _sinst;
-
- return &_sinst;
- }
-
- void Print()
- {
- cout << _x << endl;
- cout << _y << endl;
-
- for (auto& e : _vstr)
- {
- cout << e << " ";
- }
- cout << endl;
- }
-
- void AddStr(const string& s)
- {
- _vstr.push_back(s);
- }
-
- Singleton(Singleton const&) = delete;
- Singleton& operator=(Singleton const&) = delete;
-
- private:
- Singleton(int x = 0, int y = 0, const vector
& vstr = { "yyyyy","xxxx" }) - :_x(x)
- , _y(y)
- , _vstr(vstr)
- {
- cout << "Singleton()" << endl;
- }
-
- ~Singleton()
- {
- // 把数据写到文件
- cout << "~Singleton()" << endl;
- }
-
- // 想让一些数据,当前程序只有一份,那就可以把这些数据放到这个类里面
- // 再把这个类设计成单例,这个数据就只有一份了
- int _x;
- int _y;
- vector
_vstr; - };
-
- }
- int main()
- {
- cout << "xxxxxxxxx" << endl;
- cout << hunger::Singleton::GetInstance() << endl;
- cout << hunger::Singleton::GetInstance() << endl;
- cout << hunger::Singleton::GetInstance() << endl;
-
- lazy::Singleton::GetInstance()->Print();
- lazy::Singleton::GetInstance()->Print();
- lazy::Singleton::GetInstance()->Print();
-
- lazy::Singleton::GetInstance()->AddStr("甘肃");
- lazy::Singleton::GetInstance()->Print();
-
- //lazy::Singleton::DelInstance();
- cout << "xxxxxxxxx" << endl;
-
- return 0;
- }
好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。