当前位置: 首页 > news >正文

14.C++ 特殊类与设计模式

特殊类设计

  • 1. 不能被拷贝的类
  • 2. 只能在堆上创建对象的类
  • 3. 只能在栈上创建对象的类
  • 4. 设计一个类,不能被继承
  • 5. 单例模式
    • 概念
    • 饿汉模式
    • 懒汉模式
  • 6. 模版方法模式
  • 7. 工厂模式
  • 8. 观察者模式

1. 不能被拷贝的类

拷贝只会出现在两个场景中:拷贝构造和拷贝赋值。因此让该类无法调用这两个函数即可(两种方法)

  • 令这两个函数私有
  • 使用C++11关键字delete
class A
{
public:A(){}A(const A& a) = delete;  //删除函数A& operator=(const A& a) = delete;
private://A(const A& a) {}   //私有化//A& operator=(const A& a) {}
};

2. 只能在堆上创建对象的类

  1. 构造函数私有,拷贝构造私有
  2. 提供一个静态成员函数,在此函数中完成在堆上创建对象
class A
{
public:static A* CreateFronHeap(){return new A;}
private:A(const A& a) {}   //私有化A(){}
};int main()
{A* a = A::CreateFronHeap();cout << a << endl;return 0;
}

3. 只能在栈上创建对象的类

  1. 把operator new和delete这些禁用掉
  2. 提供一个静态成员函数,在此函数中完成在栈上创建对象
  • 以下为最初设想的逻辑
class A
{
public:static A CreateFronStack(){return A();}void* operator new(size_t size) = delete;void operator delete(void* ptr) = delete;void* operator new[](size_t size) = delete;void operator delete[](void* ptr) = delete;
};int main()
{A a = A::CreateFronStack();cout << &a << endl;return 0;
}

但尽管如此,由于构造函数是公有,所以还是有方法让这个类型在堆上创建(创建个新类,让A做成员)

class foo
{
public:A a;
};
int main()
{A a = A::CreateFronStack();cout << &a << endl;foo* f = new foo;cout << f << endl;cout << &f->a << endl;return 0;
}

因此,构造函数必须是私有(但是静态成员函数无法访问类内部成员);再者根据单例模式的灵感,可以变相的通过一个静态变量调用成员函数再调用构造函数

class A
{
public:static A CreateFronStack(){return _self.Create();}A Create(){return A();}void* operator new(size_t size) = delete;void operator delete(void* ptr) = delete;void* operator new[](size_t size) = delete;void operator delete[](void* ptr) = delete;
private:A(){}static A _self;
};
A A::_self = A(); //定义静态变量来调用成员函数int main()
{A a = A::CreateFronStack();A b = A::CreateFronStack();cout << &a << endl;cout << &b << endl;return 0;
}

4. 设计一个类,不能被继承

  1. 构造函数私有
class A
{
private:A(){}
};
  1. C++11的final关键字
class A final
{};
class B : public A //提示:不能将A作为基类
{};

5. 单例模式

概念

一个类只能创建一个对象,该模式保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享

饿汉模式

程序启动时就创建一个唯一的实例对象
优点:简单
缺点:可能导致进程启动慢,且如果有多个单例对象实例启动顺序不确定

class Single
{
public:static Single* GetInstance(){return &_sIns;}
private:Single(){}Single(const Single& sl) = delete;Single& operator=(const Single& sl) = delete;static Single _sIns;
};
Single Single::_sIns; //在程序入口之前就完成单例对象实例化int main()
{Single* sl = Single::GetInstance();return 0;
}

懒汉模式

程序第一次调用时才实例化对象
优点:进程启动无负载,多个单例实例启动顺序自由控制
缺点:复杂

class Single
{
public:static Single* GetInstance(){if (_sIns == nullptr) //如果单例不在,再加锁{lock_guard<mutex> lock1(_mtx);if (_sIns == nullptr) //只有一个线程会进来,其他线程阻塞在lock{_sIns.reset(new Single());}}return _sIns.get();}
private:Single(){}Single(const Single& sl) = delete;Single& operator=(const Single& sl) = delete;static unique_ptr<Single> _sIns; //单例对象static mutex _mtx; //单例对象创建时需要锁
};
unique_ptr<Single> Single::_sIns = nullptr;
mutex Single::_mtx;int main()
{Single* sl = Single::GetInstance();return 0;
}

6. 模版方法模式

模版方法定义了一个大致的框架(父类),后续继承的子类可以重新定义这个框架的步骤。
如下代码

  • 定义了做作业的父类,他只提供做作业的总步骤(DoMyHomeWork)模版方法,和下面的Write、Check、Submit都是抽象方法(纯虚函数),由子类自行实现。具体的子类ChineseHw和MathHw来实现做不同作业的差异步骤
  • 调用函数时可以使用多态
  • 父类属于抽象类,无法实例化对象
class Homework
{
public:void DoMyHomeWork() //做作业的整个步骤,但是做什么内容取决于子类{Write();Check();Submit();}virtual ~Homework(){}
public:virtual void Write() = 0;  //写作业virtual void Check() = 0;  //检查作业virtual void Submit() = 0; //提交作业
};class ChineseHw : public Homework
{
public:void Write() override{cout << "写语文作业" << endl;}void Check() override{cout << "检查语文作业" << endl;}void Submit() override{cout << "提交语文作业" << endl;}~ChineseHw(){cout << "析构语文作业对象" << endl;}
};class MathHw : public Homework
{
public:void Write() override{cout << "写数学作业" << endl;}void Check() override{cout << "检查数学作业" << endl;}void Submit() override{cout << "提交数学作业" << endl;}~MathHw() {cout << "析构数学作业对象" << endl;}
};int main()
{unique_ptr<Homework> h1(new ChineseHw);unique_ptr<Homework> h2(new MathHw);h1->DoMyHomeWork();h2->DoMyHomeWork();return 0;
}

7. 工厂模式

工厂类主要用于创建产品对象,对象的创建和使用分离,使得代码结构清晰。不需要了解产品的具体过程,只需要关系产品的使用

class Base
{
public:virtual void Print() = 0;virtual ~Base(){}
};class A : public Base
{
public:void Print() override{cout << "I am A" << endl;}~A() { cout << "~A" << endl; };
};
class B : public Base
{
public:void Print() override{cout << "I am B" << endl;}~B() { cout << "~B" << endl; };
};class Factory //工厂
{
public:static Base* CreateA() { return new A(); };static Base* CreateB() { return new B(); };
};
int main()
{Base* b1 = Factory::CreateA();Base* b2 = Factory::CreateB();b1->Print();b2->Print();delete b1;delete b2;return 0;
}

8. 观察者模式

观察者模式定义了对象之间一对多依赖关系,当一个被观察对象状态发生改变时,其他观察者对象会得到通知并自动更新

//==================== 观察者抽象类 ====================//
class Observer
{
public:virtual void update(const string& news) = 0;
};
//学生类
class Student : public Observer
{
private:string name;
public:Student(const string& n):name(n){}void update(const string& news){cout << "[Student: " << name << " ] get a news: " << news << endl;}
};
//老师类
class Teacher : public Observer
{
private:string name;
public:Teacher(const string& n) :name(n){}void update(const string& news){cout << "[Teacher: " << name << " ] get a news: " << news << endl;}
};
//==================== 被观察者抽象类 ====================//
class Subject
{
protected:vector<Observer*> _observers; //观察者的集合
public:virtual void attach(Observer* obs) //添加观察者{_observers.push_back(obs);}virtual void detach(Observer* obs)  //删除观察者{for (int i = 0; i < _observers.size(); i++){if (obs == _observers[i]){_observers.erase(_observers.begin() + i);break;}}}virtual void notify(const string& news) = 0; //通知观察者
};
//学校机构类
class SchoolAgency : public Subject
{
public:void notify(const string& news) override{for (int i = 0; i < _observers.size(); i++){_observers[i]->update(news);}}
};
int main()
{SchoolAgency agency;Student st1("张三");Teacher tc1("李四");agency.attach(&st1);agency.attach(&tc1);agency.notify("今天天气小雨,建议带伞。");return 0;
}

http://www.mrgr.cn/news/68220.html

相关文章:

  • FastDDS服务发现之PDP的收发
  • 华为android12的AlarmManager没有触发
  • 第08章 排序ORDER BY
  • 深入解析Vue3:从入门到实战(详细版)
  • Java学习教程,从入门到精通,Java 构造方法语法的知识点总结(22)
  • [大模型]视频生成-Sora简析
  • C++ 的发展
  • 青少年编程与数学 02-003 Go语言网络编程 16课题、Go语言RPC编程
  • Java集合框架之List接口
  • Halcon刚性变换
  • go语言解决rtsp协议只播放部分的问题(业务问题)
  • Android 读取内部文件
  • C++ 标准模板库 (STL)- 高效学习推荐
  • React 中 `key` 属性的警告及其解决方案
  • 初三数学,最优解问题
  • [241108] AMD 开源首批 10 亿参数语言模型:AMD OLMo | Xfce 4.20 Pre1发布
  • 【Linux系列】字符串操作的艺术:删除前缀的 Shell 脚本技巧
  • AI 浅探,揭秘【AI开发流程】的奥秘
  • Java | Leetcode Java题解之第546题移除盒子
  • 高级java每日一道面试题-2024年10月30日-JVM篇-新生代垃圾回收器和老生代垃圾回收器有哪些?有什么区别?
  • 如何实现低代码接口?低代码平台接口开发详解
  • 解决警告:Boxed value is unboxed and then immediately reboxed
  • C++:模拟实现STL的list
  • 鸿蒙NEXT开发笔记(十二)仿微信聊天App的图片转BASE64串
  • Nginx 配置文件详解
  • 【最高分数与最低分数 】