c++常用设计
设计模式
1、工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。工厂模式作为一种创建模式,一般在创建复杂对象时,考虑使用;在创建简单对象时,建议直接new完成一个实例对象的创建。
1.1、简单工厂模式
主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。使用简单工厂模式,我们只需要知道具体的产品型号就可以创建一个产品。
缺点:工厂类集中了所有产品类的创建逻辑,如果产品量较大,会使得工厂类变的非常臃肿。
/*
关键代码:创建过程在工厂类中完成。
*/
#include <iostream>
using namespace std;
//定义产品类型信息
typedef enum
{Tank_Type_56,Tank_Type_96,Tank_Type_Num
}Tank_Type;
//抽象产品类
class Tank
{
public:virtual const string& type() = 0;
};
//具体的产品类
class Tank56 : public Tank
{
public:Tank56():Tank(),m_strType("Tank56"){}
const string& type() override{cout << m_strType.data() << endl;return m_strType;}
private:string m_strType;
};
//具体的产品类
class Tank96 : public Tank
{
public:Tank96():Tank(),m_strType("Tank96"){}const string& type() override{cout << m_strType.data() << endl;return m_strType;}
private:string m_strType;
};
//工厂类
class TankFactory
{
public://根据产品信息创建具体的产品类实例,返回一个抽象产品类Tank* createTank(Tank_Type type){switch(type){case Tank_Type_56:return new Tank56();case Tank_Type_96:return new Tank96();default:return nullptr;}}
};
int main()
{TankFactory* factory = new TankFactory();Tank* tank56 = factory->createTank(Tank_Type_56);tank56->type();Tank* tank96 = factory->createTank(Tank_Type_96);tank96->type();
delete tank96;tank96 = nullptr;delete tank56;tank56 = nullptr;delete factory;factory = nullptr;
return 0;
}
1.2、工厂方法模式
定义一个创建对象的接口,其子类去具体现实这个接口以完成具体的创建工作。如果需要增加新的产品类,只需要扩展一个相应的工厂类即可。
缺点:产品类数据较多时,需要实现大量的工厂类,这无疑增加了代码量。
/*
关键代码:创建过程在其子类执行。
*/
#include <iostream>
using namespace std;
//产品抽象类
class Tank
{
public:virtual const string& type() = 0;
};
//具体的产品类
class Tank56 : public Tank
{
public:Tank56():Tank(),m_strType("Tank56"){}
const string& type() override{cout << m_strType.data() << endl;return m_strType;}
private:string m_strType;
};
//具体的产品类
class Tank96 : public Tank
{
public:Tank96():Tank(),m_strType("Tank96"){}const string& type() override{cout << m_strType.data() << endl;return m_strType;}
private:string m_strType;
};
//抽象工厂类,提供一个创建接口
class TankFactory
{
public://提供创建产品实例的接口,返回抽象产品类virtual Tank* createTank() = 0;
};
//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class Tank56Factory : public TankFactory
{
public:Tank* createTank() override{return new Tank56();}
};
//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class Tank96Factory : public TankFactory
{
public:Tank* createTank() override{return new Tank96();}
};
int main()
{TankFactory* factory56 = new Tank56Factory();Tank* tank56 = factory56->createTank();tank56->type();TankFactory* factory96 = new Tank96Factory();Tank* tank96 = factory96->createTank();tank96->type();
delete tank96;tank96 = nullptr;delete factory96;factory96 = nullptr;
delete tank56;tank56 = nullptr;delete factory56;factory56 = nullptr;
return 0;
}
1.3、抽象工厂模式
抽象工厂模式提供创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
当存在多个产品系列,而客户端只使用一个系列的产品时,可以考虑使用抽象工厂模式。
缺点:当增加一个新系列的产品时,不仅需要现实具体的产品类,还需要增加一个新的创建接口,扩展相对困难。
/*
* 关键代码:在一个工厂里聚合多个同类产品。
* 以下代码以白色衣服和黑色衣服为例,白色衣服为一个产品系列,黑色衣服为一个产品系列。白色上衣搭配白色裤子, 黑色上衣搭配黑色裤字。每个系列的衣服由一个对应的工厂创建,这样一个工厂创建的衣服能保证衣服为同一个系列。
*/
//抽象上衣类
class Coat
{
public:virtual const string& color() = 0;
};
//黑色上衣类
class BlackCoat : public Coat
{
public:BlackCoat():Coat(),m_strColor("Black Coat"){}
const string& color() override{cout << m_strColor.data() << endl;return m_strColor;}
private:string m_strColor;
};
//白色上衣类
class WhiteCoat : public Coat
{
public:WhiteCoat():Coat(),m_strColor("White Coat"){}const string& color() override{cout << m_strColor.data() << endl;return m_strColor;}
private:string m_strColor;
};
//抽象裤子类
class Pants
{
public:virtual const string& color() = 0;
};
//黑色裤子类
class BlackPants : public Pants
{
public:BlackPants():Pants(),m_strColor("Black Pants"){}const string& color() override{cout << m_strColor.data() << endl;return m_strColor;}
private:string m_strColor;
};
//白色裤子类
class WhitePants : public Pants
{
public:WhitePants():Pants(),m_strColor("White Pants"){}const string& color() override{cout << m_strColor.data() << endl;return m_strColor;}
private:string m_strColor;
};
//抽象工厂类,提供衣服创建接口
class Factory
{
public://上衣创建接口,返回抽象上衣类virtual Coat* createCoat() = 0;//裤子创建接口,返回抽象裤子类virtual Pants* createPants() = 0;
};
//创建白色衣服的工厂类,具体实现创建白色上衣和白色裤子的接口
class WhiteFactory : public Factory
{
public:Coat* createCoat() override{return new WhiteCoat();}
Pants* createPants() override{return new WhitePants();}
};
//创建黑色衣服的工厂类,具体实现创建黑色上衣和白色裤子的接口
class BlackFactory : public Factory
{Coat* createCoat() override{return new BlackCoat();}
Pants* createPants() override{return new BlackPants();}
};
2、策略模式
策略模式是指定义一系列的算法,把它们单独封装起来,并且使它们可以互相替换,使得算法可以独立于使用它的客户端而变化,也是说这些算法所完成的功能类型是一样的,对外接口也是一样的,只是不同的策略为引起环境角色环境角色表现出不同的行为。
相比于使用大量的if…else,使用策略模式可以降低复杂度,使得代码更容易维护。
缺点:可能需要定义大量的策略类,并且这些策略类都要提供给客户端。
[环境角色] 持有一个策略类的引用,最终给客户端调用。
2.1、传统的策略模式实现
/*
* 关键代码:实现同一个接口。
* 以下代码实例中,以游戏角色不同的攻击方式为不同的策略,游戏角色即为执行不同策略的环境角色。
*/
#include <iostream>
using namespace std;
//抽象策略类,提供一个接口
class Hurt
{
public:virtual void blood() = 0;
};
//具体的策略实现类,具体实现接口, Adc持续普通攻击
class AdcHurt : public Hurt
{
public:void blood() override{cout << "Adc hurt, Blood loss" << endl;}
};
//具体的策略实现类,具体实现接口, Apc技能攻击
class ApcHurt : public Hurt
{
public:void blood() override{cout << "Apc Hurt, Blood loss" << endl;}
};
//环境角色类, 游戏角色战士,传入一个策略类指针参数。
class Soldier
{
public:Soldier(Hurt* hurt):m_pHurt(hurt){}//在不同的策略下,该游戏角色表现出不同的攻击void attack(){m_pHurt->blood();}
private:Hurt* m_pHurt;
};
//定义策略标签
typedef enum
{Hurt_Type_Adc,Hurt_Type_Apc,Hurt_Type_Num
}HurtType;
//环境角色类, 游戏角色法师,传入一个策略标签参数。
class Mage
{
public:Mage(HurtType type){switch(type){case Hurt_Type_Adc:m_pHurt = new AdcHurt();break;case Hurt_Type_Apc:m_pHurt = new ApcHurt();break;default:break;}}~Mage(){delete m_pHurt;m_pHurt = nullptr;cout << "~Mage()" << endl;}
void attack(){m_pHurt->blood();}
private:Hurt* m_pHurt;
};
//环境角色类, 游戏角色弓箭手,实现模板传递策略。
template<typename T>
class Archer
{
public:void attack(){m_hurt.blood();}
private:T m_hurt;
};
int main()
{Archer<ApcHurt>* arc = new Archer<ApcHurt>;arc->attack();
delete arc;arc = nullptr;return 0;
}
2.2、使用函数指针实现策略模式
#include <iostream>
#include <functional>
void adcHurt()
{std::cout << "Adc Hurt" << std::endl;
}
void apcHurt()
{std::cout << "Apc Hurt" << std::endl;
}
//环境角色类, 使用传统的函数指针
class Soldier
{
public:typedef void (*Function)();Soldier(Function fun): m_fun(fun){}void attack(){m_fun();}
private:Function m_fun;
};
//环境角色类, 使用std::function<>
class Mage
{
public:typedef std::function<void()> Function;
Mage(Function fun): m_fun(fun){}void attack(){m_fun();}
private:Function m_fun;
};
int main()
{Soldier* soldier = new Soldier(apcHurt);soldier->attack();delete soldier;soldier = nullptr;return 0;
}
3、适配器模式
适配器模式可以将一个类的接口转换成客户端希望的另一个接口,使得原来由于接口不兼容而不能在一起工作的那些类可以在一起工作。通俗的讲就是当我们已经有了一些类,而这些类不能满足新的需求,此时就可以考虑是否能将现有的类适配成可以满足新需求的类。适配器类需要继承或依赖已有的类,实现想要的目标接口。
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
3.1、使用复合实现适配器模式
/*
* 关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
* 以下示例中,假设我们之前有了一个双端队列,新的需求要求使用栈和队列来完成。双端队列可以在头尾删减或增加元素。而栈是一种先进后出的数据结构,添加数据时添加到栈的顶部,删除数据时先删 除栈顶部的数据。因此我们完全可以将一个现有的双端队列适配成一个栈。
*/
//双端队列, 被适配类
class Deque
{
public:void push_back(int x){cout << "Deque push_back:" << x << endl;}void push_front(int x){cout << "Deque push_front:" << x << endl;}void pop_back(){cout << "Deque pop_back" << endl;}void pop_front(){cout << "Deque pop_front" << endl;}
};
//顺序类,抽象目标类
class Sequence
{
public:virtual void push(int x) = 0;virtual void pop() = 0;
};
//栈,后进先出, 适配类
class Stack:public Sequence
{
public://将元素添加到堆栈的顶部。void push(int x) override{m_deque.push_front(x);}//从堆栈中删除顶部元素void pop() override{m_deque.pop_front();}
private:Deque m_deque;
};
//队列,先进先出,适配类
class Queue:public Sequence
{
public://将元素添加到队列尾部void push(int x) override{m_deque.push_back(x);}//从队列中删除顶部元素void pop() override{m_deque.pop_front();}
private:Deque m_deque;
};
3.2、使用继承实现适配器模式
//双端队列,被适配类
class Deque
{
public:void push_back(int x){cout << "Deque push_back:" << x << endl;}void push_front(int x){cout << "Deque push_front:" << x << endl;}void pop_back(){cout << "Deque pop_back" << endl;}void pop_front(){cout << "Deque pop_front" << endl;}
};
//顺序类,抽象目标类
class Sequence
{
public:virtual void push(int x) = 0;virtual void pop() = 0;
};
//栈,后进先出, 适配类
class Stack:public Sequence, private Deque
{
public:void push(int x){push_front(x);}void pop(){pop_front();}
};
//队列,先进先出,适配类
class Queue:public Sequence, private Deque
{
public:void push(int x){push_back(x);}void pop(){pop_front();}
};
4、单例模式
单例模式顾名思义,保证一个类仅可以有一个实例化对象,并且提供一个可以访问它的全局接口。实现单例模式必须注意一下几点:
单例类只能由一个实例化对象。
单例类必须自己提供一个实例化对象。
单例类必须提供一个可以访问唯一实例化对象的接口。
单例模式分为懒汉和饿汉两种实现方式。
4.1、懒汉单例模式
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化一个对象。在访问量较小,甚至可能不会去访问的情况下,采用懒汉实现,这是以时间换空间。
4.1.1、非线程安全的懒汉单例模式
/*
* 关键代码:构造函数是私有的,不能通过赋值运算,拷贝构造等方式实例化对象。
*/
//懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
class Singleton
{
public:static Singleton* getInstance();~Singleton(){}
private:Singleton(){} //构造函数私有Singleton(const Singleton& obj) = delete; //明确拒绝Singleton& operator=(const Singleton& obj) = delete; //明确拒绝static Singleton* m_pSingleton;
};
Singleton* Singleton::m_pSingleton = NULL;
Singleton* Singleton::getInstance()
{if(m_pSingleton == NULL){m_pSingleton = new Singleton;}return m_pSingleton;
}
4.1.2、线程安全的懒汉单例模式
std::mutex mt;class Singleton
{
public:static Singleton* getInstance();
private:Singleton(){} //构造函数私有Singleton(const Singleton&) = delete; //明确拒绝Singleton& operator=(const Singleton&) = delete; //明确拒绝static Singleton* m_pSingleton;};
Singleton* Singleton::m_pSingleton = NULL;Singleton* Singleton::getInstance()
{if(m_pSingleton == NULL){mt.lock();if(m_pSingleton == NULL){m_pSingleton = new Singleton();}mt.unlock();}return m_pSingleton;
}
4.1.3、返回一个reference指向local static对象
这种单例模式实现方式多线程可能存在不确定性:任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。解决的方法:在程序的单线程启动阶段手工调用所有reference-returning函数。这种实现方式的好处是不需要去delete它。
class Singleton
{
public:static Singleton& getInstance();
private:Singleton(){}Singleton(const Singleton&) = delete; //明确拒绝Singleton& operator=(const Singleton&) = delete; //明确拒绝
};
Singleton& Singleton::getInstance()
{static Singleton singleton;return singleton;
}
4.2、饿汉单例模式
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
//饿汉式:线程安全,注意一定要在合适的地方去delete它
class Singleton
{
public:static Singleton* getInstance();
private:Singleton(){} //构造函数私有Singleton(const Singleton&) = delete; //明确拒绝Singleton& operator=(const Singleton&) = delete; //明确拒绝
static Singleton* m_pSingleton;
};
Singleton* Singleton::m_pSingleton = new Singleton();
Singleton* Singleton::getInstance()
{return m_pSingleton;
}
5、原型模式
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。通俗的讲就是当需要创建一个新的实例化对象时,我们刚好有一个实例化对象,但是已经存在的实例化对象又不能直接使用。这种情况下拷贝一个现有的实例化对象来用,可能会更方便。
以下情形可以考虑使用原型模式:
当new一个对象,非常繁琐复杂时,可以使用原型模式来进行复制一个对象。比如创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程。
当需要new一个新的对象,这个对象和现有的对象区别不大,我们就可以直接复制一个已有的对象,然后稍加修改。
当需要一个对象副本时,比如需要提供对象的数据,同时又需要避免外部对数据对象进行修改,那就拷贝一个对象副本供外部使用。
/*
* 关键代码:拷贝,return new className(*this);
*/
#include <iostream>
using namespace std;
//提供一个抽象克隆基类。
class Clone
{
public:virtual Clone* clone() = 0;virtual void show() = 0;
};
//具体的实现类
class Sheep:public Clone
{
public:Sheep(int id, string name):Clone(),m_id(id),m_name(name){cout << "Sheep() id address:" << &m_id << endl;cout << "Sheep() name address:" << &m_name << endl;}~Sheep(){}//关键代码拷贝构造函数Sheep(const Sheep& obj){this->m_id = obj.m_id;this->m_name = obj.m_name;cout << "Sheep(const Sheep& obj) id address:" << &m_id << endl;cout << "Sheep(const Sheep& obj) name address:" << &m_name << endl;}//关键代码克隆函数,返回return new Sheep(*this)Clone* clone(){return new Sheep(*this);}void show(){cout << "id :" << m_id << endl;cout << "name:" << m_name.data() << endl;}
private:int m_id;string m_name;
};
int main()
{Clone* s1 = new Sheep(1, "abs");s1->show();Clone* s2 = s1->clone();s2->show();delete s1;s1 = nullptr;delete s2;s2 = nullptr;return 0;
}
6、模板模式
模板模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
当多个类有相同的方法,并且逻辑相同,只是细节上有差异时,可以考虑使用模板模式。具体的实现上可以将相同的核心算法设计为模板方法,具体的实现细节有子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
以生产电脑为例,电脑生产的过程都是一样的,只是一些装配的器件可能不同而已。
/*
* 关键代码:在抽象类实现通用接口,细节变化在子类实现。
*/
#include <iostream>
using namespace std;
class Computer
{
public:void product(){installCpu();installRam();installGraphicsCard();}
protected:virtual void installCpu() = 0;virtual void installRam() = 0;virtual void installGraphicsCard() = 0;
};
class ComputerA : public Computer
{
protected:void installCpu() override{cout << "ComputerA install Inter Core i5" << endl;}
void installRam() override{cout << "ComputerA install 2G Ram" << endl;}
void installGraphicsCard() override{cout << "ComputerA install Gtx940 GraphicsCard" << endl;}
};
class ComputerB : public Computer
{
protected:void installCpu() override{cout << "ComputerB install Inter Core i7" << endl;}
void installRam() override{cout << "ComputerB install 4G Ram" << endl;}
void installGraphicsCard() override{cout << "ComputerB install Gtx960 GraphicsCard" << endl;}
};
int main()
{ComputerB* c1 = new ComputerB();c1->product();
delete c1;c1 = nullptr;
return 0;
}
7、建造者模式
建造者模式:将复杂对象的构建和其表示分离,使得相同的构建过程可以产生不同的表示。
以下情形可以考虑使用建造者模式:
对象的创建复杂,但是其各个部分的子对象创建算法一定。
需求变化大,构造复杂对象的子对象经常变化,但将其组合在一起的算法相对稳定。
建造者模式的优点:
将对象的创建和表示分离,客户端不需要了解具体的构建细节。
增加新的产品对象时,只需要增加其具体的建造类即可,不需要修改原来的代码,扩展方便。
产品之间差异性大,内部变化较大、较复杂时不建议使用建造者模式。
/*
*关键代码:建造者类:创建和提供实例; Director类:管理建造出来的实例的依赖关系。
*/
#include <iostream>
#include <string>
using namespace std;
//具体的产品类
class Order
{
public:void setFood(const string& food){m_strFood = food;}
const string& food(){cout << m_strFood.data() << endl;return m_strFood;}void setDrink(const string& drink){m_strDrink = drink;}
const string& drink(){cout << m_strDrink << endl;return m_strDrink;}
private:string m_strFood;string m_strDrink;
};
//抽象建造类,提供建造接口。
class OrderBuilder
{
public:virtual ~OrderBuilder(){cout << "~OrderBuilder()" << endl;}virtual void setOrderFood() = 0;virtual void setOrderDrink() = 0;virtual Order* getOrder() = 0;
};
//具体的建造类
class VegetarianOrderBuilder : public OrderBuilder
{
public:VegetarianOrderBuilder(){m_pOrder = new Order;}
~VegetarianOrderBuilder(){cout << "~VegetarianOrderBuilder()" << endl;delete m_pOrder;m_pOrder = nullptr;}
void setOrderFood() override{m_pOrder->setFood("vegetable salad");}
void setOrderDrink() override{m_pOrder->setDrink("water");}
Order* getOrder() override{return m_pOrder;}
private:Order* m_pOrder;
};
//具体的建造类
class MeatOrderBuilder : public OrderBuilder
{
public:MeatOrderBuilder(){m_pOrder = new Order;}~MeatOrderBuilder(){cout << "~MeatOrderBuilder()" << endl;delete m_pOrder;m_pOrder = nullptr;}
void setOrderFood() override{m_pOrder->setFood("beef");}
void setOrderDrink() override{m_pOrder->setDrink("beer");}
Order* getOrder() override{return m_pOrder;}
private:Order* m_pOrder;
};
//Director类,负责管理实例创建的依赖关系,指挥构建者类创建实例
class Director
{
public:Director(OrderBuilder* builder) : m_pOrderBuilder(builder){}void construct(){m_pOrderBuilder->setOrderFood();m_pOrderBuilder->setOrderDrink();}
private:OrderBuilder* m_pOrderBuilder;
};
int main()
{
// MeatOrderBuilder* mBuilder = new MeatOrderBuilder;OrderBuilder* mBuilder = new MeatOrderBuilder; //注意抽象构建类必须有虚析构函数,解析时才会 调用子类的析构函数Director* director = new Director(mBuilder);director->construct();
Order* order = mBuilder->getOrder();
order->food();
order->drink();
delete director;
director = nullptr;
delete mBuilder;
mBuilder = nullptr;
return 0;
}
外观模式:为子系统中的一组接口定义一个一致的界面;外观模式提供一个高层的接口,这个接口使得这一子系统更加容易被使用;对于复杂的系统,系统为客户端提供一个简单的接口,把负责的实现过程封装起来,客户端不需要连接系统内部的细节。
以下情形建议考虑外观模式:
设计初期阶段,应有意识的将不同层分离,层与层之间建立外观模式。
开发阶段,子系统越来越复杂,使用外观模式提供一个简单的调用接口。
一个系统可能已经非常难易维护和扩展,但又包含了非常重要的功能,可以为其开发一个外观类,使得新系统可以方便的与其交互。
优点:
实现了子系统与客户端之间的松耦合关系。
客户端屏蔽了子系统组件,减少了客户端所需要处理的对象数据,使得子系统使用起来更方便容易。
更好的划分了设计层次,对于后期维护更加的容易。
/*
* 关键代码:客户与系统之间加一个外观层,外观层处理系统的调用关系、依赖关系等。
*以下实例以电脑的启动过程为例,客户端只关心电脑开机的、关机的过程,并不需要了解电脑内部子系统的启动过程。
*/
#include <iostream>
using namespace std;
//抽象控件类,提供接口
class Control
{
public:virtual void start() = 0;virtual void shutdown() = 0;
};
//子控件, 主机
class Host : public Control
{
public:void start() override{cout << "Host start" << endl;}void shutdown() override{cout << "Host shutdown" << endl;}
};
//子控件, 显示屏
class LCDDisplay : public Control
{
public:void start() override{cout << "LCD Display start" << endl;}void shutdown() override{cout << "LCD Display shutdonw" << endl;}
};
//子控件, 外部设备
class Peripheral : public Control
{
public:void start() override{cout << "Peripheral start" << endl;}void shutdown() override{cout << "Peripheral shutdown" << endl;}
};
class Computer
{
public:void start(){m_host.start();m_display.start();m_peripheral.start();cout << "Computer start" << endl;}void shutdown(){m_host.shutdown();m_display.shutdown();m_peripheral.shutdown();cout << "Computer shutdown" << endl;}
private:Host m_host;LCDDisplay m_display;Peripheral m_peripheral;
};
int main()
{Computer computer;computer.start();
//do something
computer.shutdown();
return 0;
}
9、组合模式
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得客户端对单个对象和组合对象的使用具有一直性。
既然讲到以树形结构表示“部分-整体”,那可以将组合模式想象成一根大树,将大树分成树枝和树叶两部分,树枝上可以再长树枝,也可以长树叶,树叶上则不能再长出别的东西。
以下情况可以考虑使用组合模式:
希望表示对象的部分-整体层次结构。
希望客户端忽略组合对象与单个对象的不同,客户端将统一的使用组合结构中的所有对象。
/*
* 关键代码:树枝内部组合该接口,并且含有内部属性list,里面放Component。
*/#include <iostream>
#include <list>
#include <memory>using namespace std;//抽象类,提供组合和单个对象的一致接口
class Company
{
public:Company(const string& name): m_name(name){}virtual ~Company(){ cout << "~Company()" << endl;}virtual void add(Company* ) = 0;virtual void remove(const string&) = 0;virtual void display(int depth) = 0;virtual const string& name(){return m_name;}protected:string m_name;
};//具体的单个对象实现类,“树枝”类
class HeadCompany : public Company
{
public:HeadCompany(const string& name): Company(name){}virtual ~HeadCompany(){ cout << "~HeadCompany()" << endl;}void add(Company* company) override{shared_ptr<Company> temp(company);m_companyList.push_back(temp);}void remove(const string& strName) override{list<shared_ptr<Company>>::iterator iter = m_companyList.begin();for(; iter != m_companyList.end(); iter++){if((*iter).get()->name() == strName){//不应该在此处使用list<T>.erase(list<T>::iterator iter),会导致iter++错误,这里删除目 标元素之后,必须return。m_companyList.erase(iter);return;}}}void display(int depth) override{for(int i = 0; i < depth; i++){cout << "-";}cout << this->name().data() << endl;list<shared_ptr<Company>>::iterator iter = m_companyList.begin();for(; iter!= m_companyList.end(); iter++){(*iter).get()->display(depth + 1);}}private:list<shared_ptr<Company>> m_companyList;
};//具体的单个对象实现类,“树叶”类
class ResearchCompany : public Company
{
public:ResearchCompany(const string& name): Company(name){}virtual ~ResearchCompany(){ cout << "~ResearchCompany()" << endl;}void add(Company* ) override{}void remove(const string&) override{}void display(int depth) override{for(int i = 0; i < depth; i++){cout << "-";}cout << m_name.data() << endl;}
};//具体的单个对象实现类,“树叶”类
class SalesCompany : public Company
{
public:SalesCompany(const string& name): Company(name){}virtual ~SalesCompany(){ cout << "~SalesCompany()" << endl;}void add(Company* ) override{}void remove(const string&) override{}void display(int depth) override{for(int i = 0; i < depth; i++){cout << "-";}cout << m_name.data() << endl;}
};//具体的单个对象实现类,“树叶”类
class FinanceCompany : public Company
{
public:FinanceCompany(const string& name): Company(name){}virtual ~FinanceCompany(){ cout << "~FinanceCompany()" << endl;}void add(Company* ) override{}void remove(const string&) override{}void display(int depth) override{for(int i = 0; i < depth; i++){cout << "-";}cout << m_name.data() << endl;}
};int main()
{HeadCompany* headRoot = new HeadCompany("Head Root Company");HeadCompany* childRoot1 = new HeadCompany("Child Company A");ResearchCompany* r1 = new ResearchCompany("Research Company A");SalesCompany* s1 = new SalesCompany("Sales Company A");SalesCompany* s2 = new SalesCompany("Sales Company B");FinanceCompany* f1 = new FinanceCompany("FinanceCompany A");childRoot1->add(r1);childRoot1->add(s1);childRoot1->add(s2);childRoot1->add(f1);HeadCompany* childRoot2 = new HeadCompany("Child Company B");ResearchCompany* r2 = new ResearchCompany("Research Company B");SalesCompany* s3 = new SalesCompany("Sales Company C");SalesCompany* s4 = new SalesCompany("Sales Company D");FinanceCompany* f2 = new FinanceCompany("FinanceCompany B");childRoot2->add(r2);childRoot2->add(s3);childRoot2->add(s4);childRoot2->add(f2);headRoot->add(childRoot1);headRoot->add(childRoot2);headRoot->display(1);cout << "\n***************\n" << endl;childRoot1->remove("Sales Company B");headRoot->display(1);cout << "\n***************\n" << endl;delete headRoot;headRoot = nullptr;return 0;
}
10、代理模式
代理模式:为其它对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。
优点:
职责清晰。真实的角色只负责实现实际的业务逻辑,不用关心其它非本职责的事务,通过后期的代理完成具体的任务。这样代码会简洁清晰。
代理对象可以在客户端和目标对象之间起到中介的作用,这样就保护了目标对象。
扩展性好。
/*
- 关键代码:一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理* 类再访问真正要访问的对象。
/
#include
using namespace std;
class Gril
{
public:
Gril(const string& name = “gril”):m_string(name){}
string getName()
{
return m_string;
}
private:
string m_string;
};
class Profession
{
public:
virtual ~Profession(){}
virtual void profess() = 0;
};
class YoungMan : public Profession
{
public:
YoungMan(const Gril& gril):m_gril(gril){}
void profess()
{
cout << "Young man love " << m_gril.getName().data() << endl;
}
private:
Gril m_gril;
};
class ManProxy : public Profession
{
public:
ManProxy(const Gril& gril):m_pMan(new YoungMan(gril)){}
~ManProxy()
{
delete m_pMan;
m_pMan = nullptr;
}
void profess()
{
m_pMan->profess();
}
private:
YoungMan m_pMan;
};
int main(int argc, char argv[])
{
Gril gril(“heihei”);
ManProxy proxy = new ManProxy(gril);
proxy->profess();
delete proxy;
proxy = nullptr;
return 0;
}
11、享元模式
享元模式:运用共享技术有效地支持大量细粒度的对象。在有大量对象时,把其中共同的部分抽象出来,如果有相同的业务请求,直接返回内存中已有的对象,避免重新创建。
以下情况可以考虑使用享元模式:
系统中有大量的对象,这些对象消耗大量的内存,且这些对象的状态可以被外部化。
对于享元模式,需要将对象的信息分为两个部分:内部状态和外部状态。内部状态是指被共享出来的信息,储存在享元对象内部且不随环境变化而改变;外部状态是不可以共享的,它随环境改变而改变,是由客户端控制的。
/*
- 关键代码:将内部状态作为标识,进行共享。
/
#include
#include consumer = m_consumerMap.at(user).get();
consumer->setArticle(article);
}
else
{
shared_ptr consumer(new Consumer(user));
consumer.get()->setArticle(article);
m_consumerMap.insert(pair<string, shared_ptr>(user, consumer));
}
}
void display()
{
map<string, shared_ptr>::iterator iter = m_consumerMap.begin();
for(; iter != m_consumerMap.end(); iter++)
{
cout << iter->first.data() << " : "<< iter->second.get()->article().data() << endl;
}
}
private:
map<string, shared_ptr> m_consumerMap;
};
int main()
{
Trusteeship* ts = new Trusteeship;
ts->hosting(“zhangsan”, “computer”);
ts->hosting(“lisi”, “phone”);
ts->hosting(“wangwu”, “watch”);
ts->display();
ts->hosting(“zhangsan”, “TT”);
ts->hosting(“lisi”, “TT”);
ts->hosting(“wangwu”, “TT”);
ts->display();
delete ts;
ts = nullptr;
return 0;
}
12、桥接模式
桥接模式:将抽象部分与实现部分分离,使它们都可以独立变换。
以下情形考虑使用桥接模式:
当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现。
当多个变化因素在多个对象间共享时,考虑将这部分变化的部分抽象出来再聚合/合成进来。
当一个对象的多个变化因素可以动态变化的时候。
优点:
将实现抽离出来,再实现抽象,使得对象的具体实现依赖于抽象,满足了依赖倒转原则。
更好的可扩展性。
可动态的切换实现。桥接模式实现了抽象和实现的分离,在实现桥接模式时,就可以实现动态的选择具体的实现。
/*
- 关键代码:将现实独立出来,抽象类依赖现实类。
- 以下示例中,将各类App、各类手机独立开来,实现各种App和各种手机的自由桥接。
/
#include
using namespace std;
//抽象App类,提供接口
class App
{
public:
virtual ~App(){ cout << “~App()” << endl; }
virtual void run() = 0;
};
//具体的App实现类
class GameApp:public App
{
public:
void run()
{
cout << “GameApp Running” << endl;
}
};
//具体的App实现类
class TranslateApp:public App
{
public:
void run()
{
cout << “TranslateApp Running” << endl;
}
};
//抽象手机类,提供接口
class MobilePhone
{
public:
virtual ~MobilePhone(){ cout << “~MobilePhone()” << endl;}
virtual void appRun(App app) = 0; //实现App与手机的桥接
};
//具体的手机实现类
class XiaoMi:public MobilePhone
{
public:
void appRun(App* app)
{
cout << "XiaoMi: ";
app->run();
}
};
//具体的手机实现类
class HuaWei:public MobilePhone
{
public:
void appRun(App* app)
{
cout << "HuaWei: ";
app->run();
}
};
int main()
{
App* gameApp = new GameApp;
App* translateApp = new TranslateApp;
MobilePhone* mi = new XiaoMi;
MobilePhone* hua = new HuaWei;
mi->appRun(gameApp);
mi->appRun(translateApp);
hua->appRun(gameApp);
hua->appRun(translateApp);
delete hua;
hua = nullptr;
delete mi;
mi = nullptr;
delete gameApp;
gameApp = nullptr;
delete translateApp;
translateApp = nullptr;
return 0;
}
13、装饰模式
装饰模式:动态地给一个对象添加一些额外的功能,它是通过创建一个包装对象,也就是装饰来包裹真实的对象。新增加功能来说,装饰器模式比生产子类更加灵活。
以下情形考虑使用装饰模式:
需要扩展一个类的功能,或给一个类添加附加职责。
需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
/*
* 关键代码:1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
*/
#include <iostream>
using namespace std;
//抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
class Component
{
public:virtual ~Component(){}
virtual void configuration() = 0;
};
//具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
class Car : public Component
{
public:void configuration() override{cout << "A Car" << endl;}
};
//装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
class DecorateCar : public Component
{
public:DecorateCar(Component* car) : m_pCar(car){}
void configuration() override{m_pCar->configuration();}
private:Component* m_pCar;
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateLED : public DecorateCar
{
public:DecorateLED(Component* car) : DecorateCar(car){}
void configuration() override{DecorateCar::configuration();addLED();}
private:void addLED(){cout << "Install LED" << endl;}
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecoratePC : public DecorateCar
{
public:DecoratePC(Component* car) : DecorateCar(car){}
void configuration() override{DecorateCar::configuration();addPC();}
private:void addPC(){cout << "Install PC" << endl;}
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateEPB : public DecorateCar
{
public:DecorateEPB(Component* car) : DecorateCar(car){}
void configuration() override{DecorateCar::configuration();addEPB();}
private:void addEPB(){cout << "Install Electrical Park Brake" << endl;}
};
int main()
{Car* car = new Car;DecorateLED* ledCar = new DecorateLED(car);DecoratePC* pcCar = new DecoratePC(ledCar);DecorateEPB* epbCar = new DecorateEPB(pcCar);
epbCar->configuration();
delete epbCar;epbCar = nullptr;
delete pcCar;pcCar = nullptr;
delete ledCar;ledCar = nullptr;
delete car;car = nullptr;
return 0;
}
13、装饰模式
装饰模式:动态地给一个对象添加一些额外的功能,它是通过创建一个包装对象,也就是装饰来包裹真实的对象。新增加功能来说,装饰器模式比生产子类更加灵活。
以下情形考虑使用装饰模式:
需要扩展一个类的功能,或给一个类添加附加职责。
需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
/*
* 关键代码:1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
*/
#include <iostream>
using namespace std;
//抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
class Component
{
public:virtual ~Component(){}
virtual void configuration() = 0;
};
//具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
class Car : public Component
{
public:void configuration() override{cout << "A Car" << endl;}
};
//装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
class DecorateCar : public Component
{
public:DecorateCar(Component* car) : m_pCar(car){}
void configuration() override{m_pCar->configuration();}
private:Component* m_pCar;
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateLED : public DecorateCar
{
public:DecorateLED(Component* car) : DecorateCar(car){}
void configuration() override{DecorateCar::configuration();addLED();}
private:void addLED(){cout << "Install LED" << endl;}
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecoratePC : public DecorateCar
{
public:DecoratePC(Component* car) : DecorateCar(car){}
void configuration() override{DecorateCar::configuration();addPC();}
private:void addPC(){cout << "Install PC" << endl;}
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateEPB : public DecorateCar
{
public:DecorateEPB(Component* car) : DecorateCar(car){}
void configuration() override{DecorateCar::configuration();addEPB();}
private:void addEPB(){cout << "Install Electrical Park Brake" << endl;}
};
int main()
{Car* car = new Car;DecorateLED* ledCar = new DecorateLED(car);DecoratePC* pcCar = new DecoratePC(ledCar);DecorateEPB* epbCar = new DecorateEPB(pcCar);
epbCar->configuration();
delete epbCar;epbCar = nullptr;
delete pcCar;pcCar = nullptr;
delete ledCar;ledCar = nullptr;
delete car;car = nullptr;
return 0;
}
14、备忘录模式
备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原来保存的状态。
备忘录模式中需要定义的角色类:
Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。
/*
* 关键代码:Memento类、Originator类、Caretaker类;Originator类不与Memento类耦合,而是与Caretaker类耦合。
*/
include <iostream>
using namespace std;
//需要保存的信息
typedef struct
{int grade;string arm;string corps;
}GameValue;
//Memento类
class Memento
{
public:Memento(){}Memento(GameValue value):m_gameValue(value){}GameValue getValue(){return m_gameValue;}
private:GameValue m_gameValue;
};
//Originator类
class Game
{
public:Game(GameValue value):m_gameValue(value){}void addGrade() //等级增加{m_gameValue.grade++;}void replaceArm(string arm) //更换武器{m_gameValue.arm = arm;}void replaceCorps(string corps) //更换工会{m_gameValue.corps = corps;}Memento saveValue() //保存当前信息{Memento memento(m_gameValue);return memento;}void load(Memento memento) //载入信息{m_gameValue = memento.getValue();}void showValue(){cout << "Grade: " << m_gameValue.grade << endl;cout << "Arm : " << m_gameValue.arm.data() << endl;cout << "Corps: " << m_gameValue.corps.data() << endl;}
private:GameValue m_gameValue;
};
//Caretaker类
class Caretake
{
public:void save(Memento memento) //保存信息{m_memento = memento;}Memento load() //读已保存的信息{return m_memento;}
private:Memento m_memento;
};
int main()
{GameValue v1 = {0, "Ak", "3K"};Game game(v1); //初始值game.addGrade();game.showValue();cout << "----------" << endl;Caretake care;care.save(game.saveValue()); //保存当前值game.addGrade(); //修改当前值game.replaceArm("M16");game.replaceCorps("123");game.showValue();cout << "----------" << endl;game.load(care.load()); //恢复初始值game.showValue();return 0;
}
15、中介者模式
中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之前的交互。
如果对象与对象之前存在大量的关联关系,若一个对象改变,常常需要跟踪与之关联的对象,并做出相应的处理,这样势必会造成系统变得复杂,遇到这种情形可以考虑使用中介者模式。当多个对象存在关联关系时,为它们设计一个中介对象,当一个对象改变时,只需要通知它的中介对象,再由它的中介对象通知每个与它相关的对象
/*
* 关键代码:将相关对象的通信封装到一个类中单独处理。
*/
#include <iostream>
using namespace std;
class Mediator;
//抽象同事类。
class Businessman
{
public:Businessman(){}Businessman(Mediator* mediator) : m_pMediator(mediator){}
virtual ~Businessman(){}
virtual void setMediator(Mediator* m){m_pMediator = m;}
virtual void sendMessage(const string& msg) = 0;virtual void getMessage(const string& msg) = 0;
protected:Mediator* m_pMediator;
};
//抽象中介者类。
class Mediator
{
public:virtual ~Mediator(){}virtual void setBuyer(Businessman* buyer) = 0;virtual void setSeller(Businessman* seller) = 0;virtual void send(const string& msg, Businessman* man) = 0;
};
//具体同事类
class Buyer : public Businessman
{
public:Buyer() : Businessman(){}Buyer(Mediator* mediator) : Businessman(mediator){}
void sendMessage(const string& msg) override{m_pMediator->send(msg, this);}
void getMessage(const string& msg){cout << "Buyer recv: " << msg.data() << endl;}
};
//具体同事类
class Seller : public Businessman
{
public:Seller() : Businessman(){}Seller(Mediator* mediator) : Businessman(mediator){}
void sendMessage(const string& msg) override{m_pMediator->send(msg, this);}
void getMessage(const string& msg){cout << "Seller recv: " << msg.data() << endl;}
};
//具体中介者类
class HouseMediator : public Mediator
{
public:void setBuyer(Businessman* buyer) override{m_pBuyer = buyer;}
void setSeller(Businessman* seller) override{m_pSeller = seller;}
void send(const string& msg, Businessman* man) override{if(man == m_pBuyer){m_pSeller->getMessage(msg);}else if(man == m_pSeller){m_pBuyer->getMessage(msg);}}
private:Businessman* m_pBuyer;Businessman* m_pSeller;
};
int main()
{HouseMediator* hMediator = new HouseMediator;Buyer* buyer = new Buyer(hMediator);Seller* seller = new Seller(hMediator);
hMediator->setBuyer(buyer);hMediator->setSeller(seller);
buyer->sendMessage("Sell not to sell?");seller->sendMessage("Of course selling!");
delete buyer;buyer = nullptr;
delete seller;seller = nullptr;
delete hMediator;hMediator = nullptr;
return 0;
}
16、职责链模式
职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之前的耦合关系,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无需关心请求的处理细节和请求的传递,所有职责链将请求的发送者和请求的处理者解耦了。
/*
* 关键代码:Handler内指明其上级,handleRequest()里判断是否合适,不合适则传递给上级。
*/
#include <iostream>
using namespace std;
enum RequestLevel
{Level_One = 0,Level_Two,Level_Three,Level_Num
};
//抽象处理者(Handler)角色,提供职责链的统一接口。
class Leader
{
public:Leader(Leader* leader):m_leader(leader){}virtual ~Leader(){}virtual void handleRequest(RequestLevel level) = 0;
protected:Leader* m_leader;
};
//具体处理者(Concrete Handler)角色
class Monitor:public Leader //链扣1
{
public:Monitor(Leader* leader):Leader(leader){}void handleRequest(RequestLevel level){if(level < Level_Two){cout << "Mointor handle request : " << level << endl;}else{m_leader->handleRequest(level);}}
};
//具体处理者(Concrete Handler)角色
class Captain:public Leader //链扣2
{
public:Captain(Leader* leader):Leader(leader){}void handleRequest(RequestLevel level){if(level < Level_Three){cout << "Captain handle request : " << level << endl;}else{m_leader->handleRequest(level);}}
};
//具体处理者(Concrete Handler)角色
class General:public Leader //链扣3
{
public:General(Leader* leader):Leader(leader){}void handleRequest(RequestLevel level){cout << "General handle request : " << level << endl;}
};
int main()
{Leader* general = new General(nullptr);Leader* captain = new Captain(general);Leader* monitor = new Monitor(captain);monitor->handleRequest(Level_One);
delete monitor;monitor = nullptr;delete captain;captain = nullptr;delete general;general = nullptr;return 0;
}
17、观察者模式
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都要得到通知并自动更新。
观察者模式从根本上讲必须包含两个角色:观察者和被观察对象。
被观察对象自身应该包含一个容器来存放观察者对象,当被观察者自身发生改变时通知容器内所有的观察者对象自动更新。
观察者对象可以注册到被观察者的中,完成注册后可以检测被观察者的变化,接收被观察者的通知。当然观察者也可以被注销掉,停止对被观察者的监控。
/*
* 关键代码:在目标类中增加一个ArrayList来存放观察者们。
*/
#include <iostream>
#include <list>
#include <memory>
using namespace std;
class View;
//被观察者抽象类 数据模型
class DataModel
{
public:virtual ~DataModel(){}virtual void addView(View* view) = 0;virtual void removeView(View* view) = 0;virtual void notify() = 0; //通知函数
};
//观察者抽象类 视图
class View
{
public:virtual ~View(){ cout << "~View()" << endl; }virtual void update() = 0;virtual void setViewName(const string& name) = 0;virtual const string& name() = 0;
};
//具体的被观察类, 整数模型
class IntDataModel:public DataModel
{
public:~IntDataModel(){m_pViewList.clear();}
virtual void addView(View* view) override{shared_ptr<View> temp(view);auto iter = find(m_pViewList.begin(), m_pViewList.end(), temp);if(iter == m_pViewList.end()){m_pViewList.push_front(temp);}else{cout << "View already exists" << endl;}}
void removeView(View* view) override{auto iter = m_pViewList.begin();for(; iter != m_pViewList.end(); iter++){if((*iter).get() == view){m_pViewList.erase(iter);cout << "remove view" << endl;return;}}}
virtual void notify() override{auto iter = m_pViewList.begin();for(; iter != m_pViewList.end(); iter++){(*iter).get()->update();}}
private:list<shared_ptr<View>> m_pViewList;
};
//具体的观察者类 表视图
class TableView : public View
{
public:TableView() : m_name("unknow"){}TableView(const string& name) : m_name(name){}~TableView(){ cout << "~TableView(): " << m_name.data() << endl; }
void setViewName(const string& name){m_name = name;}
const string& name(){return m_name;}
void update() override{cout << m_name.data() << " update" << endl;}
private:string m_name;
};
int main()
{/** 这里需要补充说明的是在此示例代码中,View一旦被注册到DataModel类之后,DataModel解析时会自动解析掉 * 内部容器中存储的View对象,因此注册后的View对象不需要在手动去delete,再去delete View对象会出错。*/View* v1 = new TableView("TableView1");View* v2 = new TableView("TableView2");View* v3 = new TableView("TableView3");View* v4 = new TableView("TableView4");
IntDataModel* model = new IntDataModel;model->addView(v1);model->addView(v2);model->addView(v3);model->addView(v4);
model->notify();
cout << "-------------\n" << endl;
model->removeView(v1);
model->notify();
delete model;model = nullptr;
return 0;
}
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- 专用DNS的CDN工作流程
域名系统的前世。 域名系统之所以产生,是因为用户在198.26.92.33点10进制的IP地址访问互联网主机时记不住。因此,早在ARPANET(美国国防部高级研究计划署开发的世界上第一个运营的封包交换网络)时代,就在hosts文件中配置了主机名称与IP地址的对…...
2024/4/8 5:53:21 - 安卓rxjava如何异步使用,2021Android高级面试题,系列篇
背景 面是是上海的Android leader岗位,周日一轮面试,自我感觉答的都还不错。 结果收到邮件面试结束了,说什么"你优秀的学识和能力给我们留下了深刻的印象!经过对你综合情况的仔细评估,并经过与岗位需求进行谨慎…...
2024/4/3 14:06:02 - 安卓内存监控悬浮窗,万字Android技术类校招面试题汇总,深度好文
前言 众所周知,Android是一个基于Linux实现的操作系统。但对于Linux内核来说,Android也仅仅只是一个运行在内核之上的应用程序,与其他运行在内核之上的应用程序没有任何区别。 所以Android也需要运行环境,需要Linux内核在启动完…...
2024/4/3 17:03:33 - 大厂首发!纯干货:深度认识Sharding-JDBC!极致干货
前言 MySQL在过去由于性能高、成本低、可靠性好,已经成为最流行的开源数据库,因此被广泛地应用在Internet上的中小型网站中。随着MySQL的不断成熟,它也逐渐用于更多大规模网站和应用。非常流行的开源软件组合LAMP中的“M”指的就是MySQL。这…...
2024/4/3 14:29:09 - 事件分发机制面试题,2021年你与字节跳动只差这份笔记,先收藏了
前言 Hi~,我是 2020 届物联网专业毕业生,现就读于杭州。谨以此文来记录我的秋招以及入门前端以来的学习历程,如有错误,希望大家能及时提出! 面试情况 前前后后一共面试了 14 家公司的前端岗,按城市划分为&…...
2024/4/3 16:34:08 - 二十行代码实现的WebAR,让她能看见你的“心”
女票生日的时候,让她扫一扫你胸前的印记,跳出一个“心”给她一份感动,既省钱逼格又高🤭 内容不多,照做就好,走起εεε( ̄▽ ̄) 重点感谢,小帅哥禄禄,协助完成视…...
2024/4/28 11:00:10 - LoadRunner11打不开自带实例WebTours网页
LoadRunner11打不开自带实例WebTours网页 1、安装了LoadRunner11后,启动WebTours服务 2、访问WebTours,http://127.0.0.1:1080/WebTours/,打不开网页 3、经分析,服务正常启动了的,如下图所示...
2024/4/6 5:29:22 - CentOS 7镜像下载
官网下载链接:http://isoredirect.centos.org/centos/7/isos/x86_64/ step1: 进入下载页,选择阿里云站点进行下载 Actual Country 国内资源 Nearby Countries 周边国家资源 阿里云站点:http://mirrors.aliyun.com/cen…...
2024/4/3 15:25:34 - 12-栈空间和堆空间:数据是如何存储的
对于前端开发者来说,JavaScript 的内存机制是一个不被经常提及的概念 ,因此很容易被忽视。特别是一些非计算机专业的同学,对内存机制可能没有非常清晰的认识,甚至有些同学根本就不知道 JavaScript 的内存机制是什么 但是如果你想…...
2024/4/3 13:23:47 - ClickHouse+SuperSet安装巨坑 支持datetime以及Decimal,建议docker安装
直接结论 环境要求 系统: Centos7py3版本:python3.6, python3.7,python3.8 任意一个superset版本: 小于等于0.37.2infi.clickhouse-orm1.0.4sqlalchemy-clickhouse0.1.5.post Decimal 参考https://github.com/cloudflare/sqlalchemy-clickhouse/issues/67 建议ve…...
2024/4/3 13:24:58 - 20 行简单实现一个 unstated-next
前言 📝 👉 unstated-next 基于 React 心智模型(hookcontext)而设计的状态管理。 👈 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uhRmUffJ-1618884145855)(https://raw.githubusercontent.com/blazer233/unstat…...
2024/4/3 14:14:40 - C++初学(1)- 简单框架、输入输出流、类的创建和实例化对象
C初学 之 简单框架、输入输出、类的创建和实例化对象: 闲来无聊,学下C。(C语言底子还不错,希望学C时候可以轻松一点) 一、C的一个简单框架: #include<iostream> #include<cstdio> using name…...
2024/4/22 21:11:24 - 真无线蓝牙耳机哪个好?四款你需要知道的蓝牙耳机
真无线蓝牙耳机哪个好?四款你需要知道的蓝牙耳机 真无线蓝牙耳机是目前最主流的数码产品了,最近也收到越来越多的朋友咨询关于什么蓝牙耳机好,蓝牙耳机怎么挑选的问题。鄙人之前是烧有线耳机,对无线蓝牙耳机也做过深入了解&#…...
2024/4/3 16:12:24 - 你想学的都在这里!双非渣本Android四年磨一剑,赶紧收藏!
前言 程序员说不焦虑其实是假的,因为无论是现在还是最近几年,很早就有人察觉Android开发的野蛮生长时代已经过去。过去的优势是市场需要,这个技术少有人有,所以在抢占市场的时候,基本上满足需要就已经可以了。但是现在…...
2024/4/3 12:34:44 - 微信小程序音频IOS无法播放问题总结
【问题】 微信小程序,将文字转换为语音后,使用音频进行播放。在安卓手机下可以正常播放,在IOS下不行。 【环境】 微信小程序库版本2.3.0 百度语音合成服务 【解决方法】 正确代码: //创建一个音频实例 const myaudio wx.cr…...
2024/4/21 22:00:03 - 从零开始学习wntr day6(随机模拟泄漏)
前言 本文是通过分析WNTR包自带的stochastic_simulation例子来分析如何在随机管段上面进行添加泄漏点模拟管网漏损,希望对以后的管网模拟算例有所帮助。 一、stochastic_simulation例子代码分析 首先导入各类包: #coding utf-8 import numpy as np …...
2024/4/3 13:08:29 - USART,SPI,IIC通讯协议
这篇文章以咱们实际中最需要了解,最经常使用的方面进行说明。 1、USART通讯协议说明: USART在应用中需要注意的点有:波特率和通讯位数。一般通讯时需要RX,TX,GND三根线就可以了。 波特率指每秒钟能传多少bit,也就是1bit所需要花的…...
2024/4/4 0:28:56 - JavaScript面试题及答案
JavaScript、ES6基础面试题及答案解析②1、js基本数据类型有哪些?2、Ajax如何使用3、如何判断一个数据类型是NaN?4、闭包是什么?有什么特性?对页面有什么影响?5、谈谈js中常见的内存泄漏:6、事件委托是什么…...
2024/3/31 13:33:25 - 你想学的都在这里!被面试官问的Android问题难倒了,真香!
一、前言 这两个月听的最多的两个词就是 Android前景 和 裁员,作为被裁大军中的一员,我的关键词就是 遗憾 和 还好。 遗憾 的是现在的项目还有很多想象的空间,但是就要走向尽头;还好 的是毕业几年来一直没有以一种打工的心态在工…...
2024/4/19 18:28:06 - Java日志程序的分类以及介绍
Slf4j slf4j 的全称是 Simple Loging Facade For Java,即它仅仅是一个为 Java 程序提供日志输出的统一接 口,并不是一个具体的日志实现方案,就比如 JDBC 一样,只是一种规则而已。所以单独的 slf4j 是不 能工作的,必须…...
2024/4/7 20:11:13
最新文章
- Python 正则表达式1 函数基础
正则表达式主要函数 注:表达式指正则表达式,字符串指待处理文本。 函数名称概要re.match()匹配检查字符串是否符合表达式,返回Match对象re.search()搜索搜索字符串是否包含表达式,返回Match对象re.findall()查询查询字符串所有符…...
2024/5/4 2:21:39 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - Windows 2008虚拟机安装、安装VM Tools、快照和链接克隆、添加硬盘修改格式为GPT
一、安装vmware workstation软件 VMware workstation的安装介质,获取路径: 链接:https://pan.baidu.com/s/1AUAw_--yjZAUPbsR7StOJQ 提取码:umz1 所在目录:\vmware\VMware workstation 15.1.0 1.找到百度网盘中vmwa…...
2024/5/1 9:48:28 - 当前项目下包的管理(添加和删除)
删除当前项目虚拟环境的所有包 如果我们碰到项目中不需要的包,是不是直接使用下面这几个命令 删除单个或者多个包 pip uninstall pageName1 pageName2通过 packages.txt 删除所有的包 首先通过 pip freeze > packages.txt 命令生成 packages.txt包,…...
2024/5/4 1:49:20 - 416. 分割等和子集问题(动态规划)
题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义:dp[i][j]表示当背包容量为j,用前i个物品是否正好可以将背包填满ÿ…...
2024/5/3 11:50:27 - 【Java】ExcelWriter自适应宽度工具类(支持中文)
工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...
2024/5/2 16:04:58 - Spring cloud负载均衡@LoadBalanced LoadBalancerClient
LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon,直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件,我们讨论Spring负载均衡以Spring Cloud2020之后版本为主,学习Spring Cloud LoadBalance,暂不讨论Ribbon…...
2024/5/2 23:55:17 - TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案
一、背景需求分析 在工业产业园、化工园或生产制造园区中,周界防范意义重大,对园区的安全起到重要的作用。常规的安防方式是采用人员巡查,人力投入成本大而且效率低。周界一旦被破坏或入侵,会影响园区人员和资产安全,…...
2024/5/3 16:00:51 - VB.net WebBrowser网页元素抓取分析方法
在用WebBrowser编程实现网页操作自动化时,常要分析网页Html,例如网页在加载数据时,常会显示“系统处理中,请稍候..”,我们需要在数据加载完成后才能继续下一步操作,如何抓取这个信息的网页html元素变化&…...
2024/5/3 11:10:49 - 【Objective-C】Objective-C汇总
方法定义 参考:https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...
2024/5/3 21:22:01 - 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】
👨💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】🌏题目描述🌏输入格…...
2024/5/3 23:17:01 - 【ES6.0】- 扩展运算符(...)
【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数࿰…...
2024/5/2 23:47:43 - 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?
文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕,各大品牌纷纷晒出优异的成绩单,摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称,在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁,多个平台数据都表现出极度异常…...
2024/5/3 13:26:06 - Go语言常用命令详解(二)
文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令,这些命令可以帮助您在Go开发中进行编译、测试、运行和…...
2024/5/3 1:55:15 - 用欧拉路径判断图同构推出reverse合法性:1116T4
http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b,我们在 a i a_i ai 和 a i 1 a_{i1} ai1 之间连边, b b b 同理,则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然࿰…...
2024/5/4 2:14:16 - 【NGINX--1】基础知识
1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息,并安装一些有助于配置官方 NGINX 软件包仓库的软件包: apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...
2024/5/3 16:23:03 - Hive默认分割符、存储格式与数据压缩
目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...
2024/5/3 1:55:09 - 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法
文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中,传感器和控制器产生大量周…...
2024/5/2 8:37:00 - --max-old-space-size=8192报错
vue项目运行时,如果经常运行慢,崩溃停止服务,报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中,通过JavaScript使用内存时只能使用部分内存(64位系统&…...
2024/5/3 14:57:24 - 基于深度学习的恶意软件检测
恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞,例如可以被劫持的合法软件(例如浏览器或 Web 应用程序插件)中的错误。 恶意软件渗透可能会造成灾难性的后果,包括数据被盗、勒索或网…...
2024/5/2 9:47:25 - JS原型对象prototype
让我简单的为大家介绍一下原型对象prototype吧! 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象…...
2024/5/4 2:00:16 - C++中只能有一个实例的单例类
C中只能有一个实例的单例类 前面讨论的 President 类很不错,但存在一个缺陷:无法禁止通过实例化多个对象来创建多名总统: President One, Two, Three; 由于复制构造函数是私有的,其中每个对象都是不可复制的,但您的目…...
2024/5/3 22:03:11 - python django 小程序图书借阅源码
开发工具: PyCharm,mysql5.7,微信开发者工具 技术说明: python django html 小程序 功能介绍: 用户端: 登录注册(含授权登录) 首页显示搜索图书,轮播图࿰…...
2024/5/3 7:43:42 - 电子学会C/C++编程等级考试2022年03月(一级)真题解析
C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...
2024/5/3 1:54:59 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57