设计模式的书相信很多人都看过。对于设计模式这样一种方法,相信不同的人有不同的理解。

 

C语言和设计模式(开篇)

关于软件设计方面的书很多,比如《重构》,比如《设计模式》。至于软件开发方式,那就更多了,什么极限编程、精益方法、敏捷方法。随着时间的推移,很多的方法又会被重新提出来。


其实,就我个人看来,不管什么方法都离不开人。一个人写不出二叉树,你怎么让他写?敏捷吗?你写一行,我写一行。还是迭代?写三行,删掉两行,再写三行。项目的成功是偶然的,但是项目的失败却有很多原因,管理混乱、需求混乱、设计低劣、代码质量差、测试不到位等等。就软件企业而言,没有比优秀的文化和出色的企业人才更重要的了。


从软件设计层面来说,一般来说主要包括三个方面:

(1)软件的设计受众,是小孩子、老人、女性,还是专业人士等等;
(2)软件的基本设计原则,以人为本、模块分离、层次清晰、简约至上、适用为先、抽象基本业务等等;
(3)软件编写模式,比如装饰模式、责任链、单件模式等等。

从某种意义上说,设计思想构成了软件的主题。软件原则是我们在开发中的必须遵循的准绳。软件编写模式是开发过程中的重要经验总结。灵活运用设计模式,一方面利于我们编写高质量的代码,另一方面也方便我们对代码进行维护。毕竟对于广大的软件开发者来说,软件的维护时间要比软件编写的时间要多得多。编写过程中,难免要有新的需求,要和别的模块打交道,要对已有的代码进行复用,那么这时候设计模式就派上了用场。我们讨论的主题其实就是设计模式。

讲到设计模式,人们首先想到的语言就是c#或者是java,最不济也是c++,一般来说没有人会考虑到c语言。其实,我认为设计模式就是一种基本思想,过度美化或者神化其实没有必要。其实阅读过linux kernel的朋友都知道,linux虽然自身支持很多的文件系统,但是linux自身很好地把这些系统的基本操作都抽象出来了,成为了基本的虚拟文件系统。

举个例子来说,现在让你写一个音乐播放器,但是要支持的文件格式很多,什么ogg,wav,mp3啊,统统要支持。这时候,你会怎么编写呢?如果用C++语言,你可能会这么写。

[cpp] view plaincopy
  1. class music_file
  2. {
  3. HANDLE hFile;
  4. public:
  5. void music_file() {}
  6. virtual ~music_file() {}
  7. virtual void read_file() {}
  8. virtual void play() {}
  9. virtual void stop() {}
  10. virtual void back() {}
  11. virtual void front() {}
  12. virtual void up() {}
  13. virtual void down() {}
  14. };
其实,你想想看,如果用C语言能够完成相同的抽象操作,那不是效果一样的吗?

[cpp] view plaincopy
  1. typedef struct _music_file
  2. {
  3. HANDLE hFile;
  4. void (*read_file)(struct _music_file* pMusicFile);
  5. void (*play)(struct _music_file* pMusicFile);
  6. void (*stop)(struct _music_file* pMusicFile);
  7. void (*back)(struct _music_file* pMusicFile);
  8. void (*front)(struct _music_file* pMusicFile);
  9. void (*down)(struct _music_file* pMusicFile);
  10. void (*up)(struct _music_file* pMusicFile);
  11. }music_file;
当然,上面的例子比较简单,但是也能说明一些问题。写这篇文章的目的一是希望和朋友们共同学习模式的相关内容,另一方面也希望朋友们能够活学活用,既不要迷信权威,也不要妄自菲薄。只要付出努力,付出汗水,肯定会有收获的。有些大环境你改变不了,那就从改变自己开始。万丈高楼平地起,一步一个脚印才能真真实实学到东西。如果盲目崇拜,言必google、微软、apple,那么除了带来几个唾沫星,还能有什么受用呢?无非白费了口舌而已。

希望和大家共勉。


C语言和设计模式(单件模式)


有过面试经验的朋友,或者对设计模式有点熟悉的朋友,都会对单件模式不陌生。对很多面试官而言,单件模式更是他们面试的保留项目。其实,我倒认为,单件模式算不上什么设计模式。最多也就是个技巧。

单件模式要是用C++写,一般这么写。

[cpp] view plaincopy
  1. #include <string.h>
  2. #include <assert.h>
  3. class object
  4. {
  5. public:
  6. static class object* pObject;
  7. static object* create_new_object()
  8. {
  9. if(NULL != pObject)
  10. return pObject;
  11. pObject = new object();
  12. assert(NULL != pObject);
  13. return pObject;
  14. }
  15. private:
  16. object() {}
  17. ~object() {}
  18. };
  19. class object* object::pObject = NULL;

单件模式的技巧就在于类的构造函数是一个私有的函数。但是类的构造函数又是必须创建的?怎么办呢?那就只有动用static函数了。我们看到static里面调用了构造函数,就是这么简单。

[cpp] view plaincopy
  1. int main(int argc,char* argv[])
  2. {
  3. object* pGlobal = object::create_new_object();
  4. return 1;
  5. }

 上面说了C++语言的编写方法,那C语言怎么写?其实也简单。大家也可以试一试。

[cpp] view plaincopy
  1. typedef struct _DATA
  2. {
  3. void* pData;
  4. }DATA;
  5. void* get_data()
  6. {
  7. static DATA* pData = NULL;
  8. if(NULL != pData)
  9. return pData;
  10. pData = (DATA*)malloc(sizeof(DATA));
  11. assert(NULL != pData);
  12. return (void*)pData;
  13. }

C语言和设计模式(之原型模式)

 

原型模式本质上说就是对当前数据进行复制。就像变戏法一样,一个鸽子变成了两个鸽子,两个鸽子变成了三个鸽子,就这么一直变下去。在变的过程中,我们不需要考虑具体的数据类型。为什么呢?因为不同的数据有自己的复制类型,而且每个复制函数都是虚函数。

用C++怎么编写呢,那就是先写一个基类,再编写一个子类。就是这么简单。

[cpp] view plaincopy
  1. class data
  2. {
  3. public:
  4. data () {}
  5. virtual ~data() {}
  6. virtual class data* copy() = 0;
  7. };
  8. class data_A : public data
  9. {
  10. public:
  11. data_A() {}
  12. ~data_A() {}
  13. class data* copy()
  14. {
  15. return new data_A();
  16. }
  17. };
  18. class data_B : public data
  19. {
  20. public:
  21. data_B() {}
  22. ~data_B() {}
  23. class data* copy()
  24. {
  25. return new data_B();
  26. }
  27. };
那怎么使用呢?其实只要一个通用的调用接口就可以了。
[cpp] view plaincopy
  1. class data* clone(class data* pData)
  2. {
  3. return pData->copy();
  4. }
就这么简单的一个技巧,对C来说,当然也不是什么难事。

[cpp] view plaincopy
  1. typedef struct _DATA
  2. {
  3. struct _DATA* (*copy) (struct _DATA* pData);
  4. }DATA;
假设也有这么一个类型data_A,

[cpp] view plaincopy
  1. DATA data_A = {data_copy_A};
既然上面用到了这个函数,所以我们也要定义啊。

[cpp] view plaincopy
  1. struct _DATA* data_copy_A(struct _DATA* pData)
  2. {
  3. DATA* pResult = (DATA*)malloc(sizeof(DATA));
  4. assert(NULL != pResult);
  5. memmove(pResult, pData, sizeof(DATA));
  6. return pResult;
  7. };
使用上呢,当然也不含糊。

[cpp] view plaincopy
  1. struct _DATA* clone(struct _DATA* pData)
  2. {
  3. return pData->copy(pData);
  4. };

C语言和设计模式(之组合模式)

组合模式听说去很玄乎,其实也并不复杂。为什么?大家可以先想一下数据结构里面的二叉树是怎么回事。为什么就是这么一个简单的二叉树节点既可能是叶节点,也可能是父节点?

[cpp] view plaincopy
  1. typedef struct _NODE
  2. {
  3. void* pData;
  4. struct _NODE* left;
  5. struct _NODE* right;
  6. }NODE;
那什么时候是叶子节点,其实就是left、right为NULL的时候。那么如果它们不是NULL呢,那么很明显此时它们已经是父节点了。那么,我们的这个组合模式是怎么一个情况呢?

[cpp] view plaincopy
  1. typedef struct _Object
  2. {
  3. struct _Object** ppObject;
  4. int number;
  5. void (*operate)(struct _Object* pObject);
  6. }Object;
就是这么一个简单的数据结构,是怎么实现子节点和父节点的差别呢。比如说,现在我们需要对一个父节点的operate进行操作,此时的operate函数应该怎么操作呢?
[cpp] view plaincopy
  1. void operate_of_parent(struct _Object* pObject)
  2. {
  3. int index;
  4. assert(NULL != pObject);
  5. assert(NULL != pObject->ppObject && 0 != pObject->number);
  6. for(index = 0; index < pObject->number; index ++)
  7. {
  8. pObject->ppObject[index]->operate(pObject->ppObject[index]);
  9. }
  10. }
当然,有了parent的operate,也有child的operate。至于是什么操作,那就看自己是怎么操作的了。

[cpp] view plaincopy
  1. void operate_of_child(struct _Object* pObject)
  2. {
  3. assert(NULL != pObject);
  4. printf("child node!\n");
  5. }
父节点也好,子节点也罢,一切的一切都是最后的应用。其实,用户的调用也非常简单,就这么一个简单的函数。

[cpp] view plaincopy
  1. void process(struct Object* pObject)
  2. {
  3. assert(NULL != pObject);
  4. pObject->operate(pObject);
  5. }

C语言和设计模式(之模板模式)

模板对于学习C++的同学,其实并不陌生。函数有模板函数,类也有模板类。那么这个模板模式是个什么情况?我们可以思考一下,模板的本质是什么。比如说,现在我们需要编写一个简单的比较模板函数。

 

 模板函数提示我们,只要比较的逻辑是确定的,那么不管是什么数据类型,都会得到一个相应的结果。固然,这个比较的流程比较简单,即使没有采用模板函数也没有关系。但是,要是需要拆分的步骤很多,那么又该怎么办呢?如果相通了这个问题,那么也就明白了什么是template模式。

 

比方说,现在我们需要设计一个流程。这个流程有很多小的步骤完成。然而,其中每一个步骤的方法是多种多样的,我们可以很多选择。但是,所有步骤构成的逻辑是唯一的,那么我们该怎么办呢?其实也简单。那就是在基类中除了流程函数外,其他的步骤函数全部设置为virtual函数即可。

 

 basic的类说明了基本的流程process是唯一的,所以我们要做的就是对step1和step2进行改写。

 

 

 所以,按照我个人的理解,这里的template主要是一种流程上的统一,细节实现上的分离。明白了这个思想,那么用C语言来描述template模式就不是什么难事了。
 因为在C++中process函数是直接继承的,C语言下面没有这个机制。所以,对于每一个process来说,process函数都是唯一的,但是我们每一次操作的时候还是要去复制一遍函数指针。而step1和step2是不同的,所以各种方法可以用来灵活修改自己的处理逻辑,没有问题。

 

 

 

C语言和设计模式(工厂模式)

工厂模式是比较简单,也是比较好用的一种方式。根本上说,工厂模式的目的就根据不同的要求输出不同的产品。比如说吧,有一个生产鞋子的工厂,它能生产皮鞋,也能生产胶鞋。如果用代码设计,应该怎么做呢?

 

[cpp] view plaincopy
  1. typedef struct _Shoe
  2. {
  3. int type;
  4. void (*print_shoe)(struct _Shoe*);
  5. }Shoe;

 就像上面说的,现在有胶鞋,那也有皮鞋,我们该怎么做呢?

 

 

[cpp] view plaincopy
  1. void print_leather_shoe(struct _Shoe* pShoe)
  2. {
  3. assert(NULL != pShoe);
  4. printf("This is a leather show!\n");
  5. }
  6. void print_rubber_shoe(struct _Shoe* pShoe)
  7. {
  8. assert(NULL != pShoe);
  9. printf("This is a rubber shoe!\n");
  10. }

 所以,对于一个工厂来说,创建什么样的鞋子,就看我们输入的参数是什么?至于结果,那都是一样的。

 

 

[cpp] view plaincopy
  1. #define LEATHER_TYPE 0x01
  2. #define RUBBER_TYPE 0x02
  3. Shoe* manufacture_new_shoe(int type)
  4. {
  5. assert(LEATHER_TYPE == type || RUBBER_TYPE == type);
  6. Shoe* pShoe = (Shoe*)malloc(sizeof(Shoe));
  7. assert(NULL != pShoe);
  8. memset(pShoe, 0, sizeof(Shoe));
  9. if(LEATHER_TYPE == type)
  10. {
  11. pShoe->type == LEATHER_TYPE;
  12. pShoe->print_shoe = print_leather_shoe;
  13. }
  14. else
  15. {
  16. pShoe->type == RUBBER_TYPE;
  17. pShoe->print_shoe = print_rubber_shoe;
  18. }
  19. return pShoe;
  20. }

 

C语言和设计模式(责任链模式)

责任链模式是很实用的一种实际方法。举个例子来说,我们平常在公司里面难免不了报销流程。但是,我们知道公司里面每一级的领导的报批额度是不一样的。比如说,科长的额度是1000元,部长是10000元,总经理是10万元。

那么这个时候,我们应该怎么设计呢?其实可以这么理解。比如说,有人来找领导报销费用了,那么领导可以自己先看看自己能不能报。如果费用可以顺利报下来当然最好,可是万一报不下来呢?那就只能请示领导的领导了。

[cpp] view plaincopy
  1. typedef struct _Leader
  2. {
  3. struct _Leader* next;
  4. int account;
  5. int (*request)(strcut _Leader* pLeader,int num);
  6. }Leader;

 所以这个时候,我们首先需要设置额度和领导。

[cpp] view plaincopy
  1. void set_account(struct _Leader* pLeader,int account)
  2. {
  3. assert(NULL != pLeader);
  4. pLeader->account = account;
  5. return;
  6. }
  7. void set_next_leader(conststruct _Leader* pLeader, struct _Leader* next)
  8. {
  9. assert(NULL != pLeader && NULL != next);
  10. pLeader->next = next;
  11. return;
  12. }

 此时,如果有一个员工过来报销费用,那么应该怎么做呢?假设此时的Leader是经理,报销额度是10万元。所以此时,我们可以看看报销的费用是不是小于10万元?少于这个数就OK,反之就得上报自己的领导了。

[cpp] view plaincopy
  1. int request_for_manager(struct _Leader* pLeader,int num)
  2. {
  3. assert(NULL != pLeader && 0 != num);
  4. if(num < 100000)
  5. return 1;
  6. else if(pLeader->next)
  7. return pLeader->next->request(pLeader->next, num);
  8. else
  9. return 0;
  10. }

C语言和设计模式(抽象工厂模式)

前面我们写过的工厂模式实际上是对产品的抽象。对于不同的用户需求,我们可以给予不同的产品,而且这些产品的接口都是一致的。而抽象工厂呢?顾名思义,就是说我们的工厂是不一定的。怎么理解呢,举个例子。

假设有两个水果店都在卖水果,都卖苹果和葡萄。其中一个水果店买白苹果和白葡萄,另外一个水果店卖红苹果和红葡萄。所以说,对于水果店而言,尽管都在卖水果,但是两个店卖的品种不一样。

既然水果不一样,那我们先定义水果。

[cpp] view plaincopy
  1. typedef struct _Apple
  2. {
  3. void (*print_apple)();
  4. }Apple;
  5. typedef struct _Grape
  6. {
  7. void (*print_grape)();
  8. }Grape;
上面分别对苹果和葡萄进行了抽象,当然它们的具体函数也是不一样的。

[cpp] view plaincopy
  1. void print_white_apple()
  2. {
  3. printf("white apple!\n");
  4. }
  5. void print_red_apple()
  6. {
  7. printf("red apple!\n");
  8. }
  9. void print_white_grape()
  10. {
  11. printf("white grape!\n");
  12. }
  13. void print_red_grape()
  14. {
  15. printf("red grape!\n");
  16. }
完成了水果函数的定义。下面就该定义工厂了,和水果一样,我们也需要对工厂进行抽象处理。

[cpp] view plaincopy
  1. typedef struct _FruitShop
  2. {
  3. Apple* (*sell_apple)();
  4. Apple* (*sell_grape)();
  5. }FruitShop;
所以,对于卖白苹果、白葡萄的水果店就该这样设计了,红苹果、红葡萄的水果店亦是如此。

[cpp] view plaincopy
  1. Apple* sell_white_apple()
  2. {
  3. Apple* pApple = (Apple*) malloc(sizeof(Apple));
  4. assert(NULL != pApple);
  5. pApple->print_apple = print_white_apple;
  6. return pApple;
  7. }
  8. Grape* sell_white_grape()
  9. {
  10. Grape* pGrape = (Grape*) malloc(sizeof(Grape));
  11. assert(NULL != pGrape);
  12. pGrape->print_grape = print_white_grape;
  13. return pGrape;
  14. }
这样,基本的框架就算搭建完成的,以后创建工厂的时候,

[cpp] view plaincopy
  1. FruitShop* create_fruit_shop(int color)
  2. {
  3. FruitShop* pFruitShop = (FruitShop*) malloc(sizeof(FruitShop));
  4. assert(NULL != pFruitShop);
  5. if(WHITE == color)
  6. {
  7. pFruitShop->sell_apple = sell_white_apple;
  8. pFruitShop->sell_grape = sell_white_grape;
  9. }
  10. else
  11. {
  12. pFruitShop->sell_apple = sell_red_apple;
  13. pFruitShop->sell_grape = sell_red_grape;
  14. }
  15. return pFruitShop;
  16. }

C语言和设计模式(迭代器模式)

使用过C++的朋友大概对迭代器模式都不会太陌生。这主要是因为我们在编写代码的时候离不开迭代器,队列有迭代器,向量也有迭代器。那么,为什么要迭代器呢?这主要是为了提炼一种通用的数据访问方法。

比如说,现在有一个数据的容器,

 

[cpp] view plaincopy
  1. typedef struct _Container
  2. {
  3. int* pData;
  4. int size;
  5. int length;
  6. Interator* (*create_new_interator)(struct _Container* pContainer);
  7. int (*get_first)(struct _Container* pContainer);
  8. int (*get_last)(struct _Container* pContainer);
  9. }Container;

 我们看到,容器可以创建迭代器。那什么是迭代器呢?

 

 

[cpp] view plaincopy
  1. typedef struct _Interator
  2. {
  3. void* pVector;
  4. int index;
  5. int(* get_first)(struct _Interator* pInterator);
  6. int(* get_last)(struct _Interator* pInterator);
  7. }Interator;

 我们看到,容器有get_first,迭代器也有get_first,这中间有什么区别?

 

 

[cpp] view plaincopy
  1. int vector_get_first(struct _Container* pContainer)
  2. {
  3. assert(NULL != pContainer);
  4. return pContainer->pData[0];
  5. }
  6. int vector_get_last(struct _Container* pContainer)
  7. {
  8. assert(NULL != pContainer);
  9. return pContainer->pData[pContainer->size -1];
  10. }
  11. int vector_interator_get_first(struct _Interator* pInterator)
  12. {
  13. Container* pContainer;
  14. assert(NULL != pInterator && NULL != pInterator->pVector);
  15. pContainer = (struct _Container*) (pInterator->pVector);
  16. return pContainer ->get_first(pContainer);
  17. }
  18. int vector_interator_get_last(struct _Interator* pInterator)
  19. {
  20. Container* pContainer;
  21. assert(NULL != pInterator && NULL != pInterator->pVector);
  22. pContainer = (struct _Container*) (pInterator->pVector);
  23. return pContainer ->get_last(pContainer);
  24. }

 看到上面的代码之后,我们发现迭代器的操作实际上也是对容器的操作而已。

C语言和设计模式(外观模式)

外观模式是比较简单的模式。它的目的也是为了简单。什么意思呢?举个例子吧。以前,我们逛街的时候吃要到小吃一条街,购物要到购物一条街,看书、看电影要到文化一条街。那么有没有这样的地方,既可以吃喝玩乐,同时相互又靠得比较近呢。其实,这就是悠闲广场,遍布全国的万达广场就是干了这么一件事。

首先,我们原来是怎么做的。

[cpp] view plaincopy
  1. typedef struct _FoodSteet
  2. {
  3. void (*eat)();
  4. }FoodStreet;
  5. void eat()
  6. {
  7. printf("eat here!\n");
  8. }
  9. typedef struct _ShopStreet
  10. {
  11. void (*buy)();
  12. }ShopStreet;
  13. void buy()
  14. {
  15. printf("buy here!\n");
  16. }
  17. typedef struct _BookStreet
  18. {
  19. void (*read)();
  20. }BookStreet;
  21. void read()
  22. {
  23. printf("read here");
  24. }

 下面,我们就要在一个plaza里面完成所有的项目,怎么办呢?

[cpp] view plaincopy
  1. typedef struct _Plaza
  2. {
  3. FoodStreet* pFoodStreet;
  4. ShopStreet* pShopStreet;
  5. BookStreet* pBookStreet;
  6. void (*play)(struct _Plaza* pPlaza);
  7. }Plaza;
  8. void play(struct _Plaza* pPlaza)
  9. {
  10. assert(NULL != pPlaza);
  11. pPlaza->pFoodStreet->eat();
  12. pPlaza->pShopStreet->buy();
  13. pPlaza->pBookStreet->read();
  14. }

C语言和设计模式(代理模式)

代理模式是一种比较有意思的设计模式。它的基本思路也不复杂。举个例子来说,以前在学校上网的时候,并不是每一台pc都有上网的权限的。比如说,现在有pc1、pc2、pc3,但是只有pc1有上网权限,但是pc2、pc3也想上网,此时应该怎么办呢?

此时,我们需要做的就是在pc1上开启代理软件,同时把pc2、pc3的IE代理指向pc1即可。这个时候,如果pc2或者pc3想上网,那么报文会先指向pc1,然后pc1把Internet传回的报文再发给pc2或者pc3。这样一个代理的过程就完成了整个的上网过程。

在说明完整的过程之后,我们可以考虑一下软件应该怎么编写呢?

[cpp] view plaincopy
  1. typedef struct _PC_Client
  2. {
  3. void (*request)();
  4. }PC_Client;
  5. void ftp_request()
  6. {
  7. printf("request from ftp!\n");
  8. }
  9. void http_request()
  10. {
  11. printf("request from http!\n");
  12. }
  13. void smtp_request()
  14. {
  15. printf("request from smtp!\n");
  16. }

这个时候,代理的操作应该怎么写呢?怎么处理来自各个协议的请求呢?

[cpp] view plaincopy
  1. typedef struct _Proxy
  2. {
  3. PC_Client* pClient;
  4. }Proxy;
  5. void process(Proxy* pProxy)
  6. {
  7. assert(NULL != pProxy);
  8. pProxy->pClient->request();
  9. }

C语言和设计模式(享元模式)

享元模式看上去有点玄乎,但是其实也没有那么复杂。我们还是用示例说话。比如说,大家在使用电脑的使用应该少不了使用WORD软件。使用WORD呢, 那就少不了设置模板。什么模板呢,比如说标题的模板,正文的模板等等。这些模板呢,又包括很多的内容。哪些方面呢,比如说字体、标号、字距、行距、大小等等。

[cpp] view plaincopy
  1. typedef struct _Font
  2. {
  3. int type;
  4. int sequence;
  5. int gap;
  6. int lineDistance;
  7. void (*operate)(struct _Font* pFont);
  8. }Font;

 上面的Font表示了各种Font的模板形式。所以,下面的方法就是定制一个FontFactory的结构。

[cpp] view plaincopy
  1. typedef struct _FontFactory
  2. {
  3. Font** ppFont;
  4. int number;
  5. int size;
  6. Font* GetFont(struct _FontFactory* pFontFactory,int type, int sequence,int gap, int lineDistance);
  7. }FontFactory;

 

这里的GetFont即使对当前的Font进行判断,如果Font存在,那么返回;否则创建一个新的Font模式。

[cpp] view plaincopy
  1. Font* GetFont(struct _FontFactory* pFontFactory,int type, int sequence,int gap, int lineDistance)
  2. {
  3. int index;
  4. Font* pFont;
  5. Font* ppFont;
  6. if(NULL == pFontFactory)
  7. return NULL;
  8. for(index = 0; index < pFontFactory->number; index++)
  9. {
  10. if(type != pFontFactory->ppFont[index]->type)
  11. continue;
  12. if(sequence != pFontFactory->ppFont[index]->sequence)
  13. continue;
  14. if(gap != pFontFactory->ppFont[index]->gap)
  15. continue;
  16. if(lineDistance != pFontFactory->ppFont[index]->lineDistance)
  17. continue;
  18. return pFontFactory->ppFont[index];
  19. }
  20. pFont = (Font*)malloc(sizeof(Font));
  21. assert(NULL != pFont);
  22. pFont->type = type;
  23. pFont->sequence = sequence;
  24. pFont->gap = gap;
  25. pFont->lineDistance = lineDistance;
  26. if(pFontFactory-> number < pFontFactory->size)
  27. {
  28. pFontFactory->ppFont[index] = pFont;
  29. pFontFactory->number ++;
  30. return pFont;
  31. }
  32. ppFont = (Font**)malloc(sizeof(Font*) * pFontFactory->size * 2);
  33. assert(NULL != ppFont);
  34. memmove(ppFont, pFontFacoty->ppFont, pFontFactory->size);
  35. free(pFontFactory->ppFont);
  36. pFontFactory->size *= 2;
  37. pFontFactory->number ++;
  38. ppFontFactory->ppFont = ppFont;
  39. return pFont;
  40. }

C语言和设计模式(装饰模式)

装饰模式是比较好玩,也比较有意义。其实就我个人看来,它和责任链还是蛮像的。只不过一个是比较判断,一个是迭代处理。装饰模式就是那种迭代处理的模式,关键在哪呢?我们可以看看数据结构。

[cpp] view plaincopy
  1. typedef struct _Object
  2. {
  3. struct _Object* prev;
  4. void (*decorate)(struct _Object* pObject);
  5. }Object;

 装饰模式最经典的地方就是把pObject这个值放在了数据结构里面。当然,装饰模式的奥妙还不仅仅在这个地方,还有一个地方就是迭代处理。我们可以自己随便写一个decorate函数试试看,

[cpp] view plaincopy
  1. void decorate(struct _Object* pObeject)
  2. {
  3. assert(NULL != pObject);
  4. if(NULL != pObject->prev)
  5. pObject->prev->decorate(pObject->prev);
  6. printf("normal decorate!\n");
  7. }

 所以,装饰模式的最重要的两个方面就体现在:prev参数和decorate迭代处理。

C语言和设计模式(适配器模式)

现在的生活当中,我们离不开各种电子工具。什么笔记本电脑、手机、mp4啊,都离不开充电。既然是充电,那么就需要用到充电器。其实从根本上来说,充电器就是一个个普通的适配器。什么叫适配器呢,就是把220v、50hz的交流电压编程5~12v的直流电压。充电器就干了这么一件事情。
那么,这样的一个充电适配器,我们应该怎么用c++描述呢?

[cpp] view plaincopy
  1. class voltage_12v
  2. {
  3. public:
  4. voltage_12v() {}
  5. virtual ~voltage_12v() {}
  6. virtual void request() {}
  7. };
  8. class v220_to_v12
  9. {
  10. public:
  11. v220_to_v12() {}
  12. ~v220_to_v12() {}
  13. void voltage_transform_process() {}
  14. };
  15. class adapter: public voltage_12v
  16. {
  17. v220_to_v12* pAdaptee;
  18. public:
  19. adapter() {}
  20. ~adapter() {}
  21. void request()
  22. {
  23. pAdaptee->voltage_transform_process();
  24. }
  25. };

 过上面的代码,我们其实可以这样理解。类voltage_12v表示我们的最终目的就是为了获得一个12v的直流电压。当然获得12v可以有很多的方法,利用适配器转换仅仅是其中的一个方法。adapter表示适配器,它自己不能实现220v到12v的转换工作,所以需要调用类v220_to_v12的转换函数。所以,我们利用adapter获得12v的过程,其实就是调用v220_to_v12函数的过程。

不过,既然我们的主题是用c语言来编写适配器模式,那么我们就要实现最初的目标。这其实也不难,关键一步就是定义一个Adapter的数据结构。然后把所有的Adapter工作都由Adaptee来做,就是这么简单。不知我说明白了没有?

[cpp] view plaincopy
  1. typdef struct _Adaptee
  2. {
  3. void (*real_process)(struct _Adaptee* pAdaptee);
  4. }Adaptee;
  5. typedef struct _Adapter
  6. {
  7. void* pAdaptee;
  8. void (*transform_process)(struct _Adapter* pAdapter);
  9. }Adapter;

 C语言和设计模式(策略模式)

策略模式就是用统一的方法接口分别对不同类型的数据进行访问。比如说,现在我们想用pc看一部电影,此时应该怎么做呢?看电影嘛,当然需要各种播放电影的方法。rmvb要rmvb格式的方法,avi要avi的方法,mpeg要mpeg的方法。可是事实上,我们完全可以不去管是什么文件格式。因为播放器对所有的操作进行了抽象,不同的文件会自动调用相应的访问方法。

[cpp] view plaincopy
  1. typedef struct _MoviePlay
  2. {
  3. struct _CommMoviePlay* pCommMoviePlay;
  4. }MoviePlay;
  5. typedef struct _CommMoviePlay
  6. {
  7. HANDLE hFile;
  8. void (*play)(HANDLE hFile);
  9. }CommMoviePlay;

 这个时候呢,对于用户来说,统一的文件接口就是MoviePlay。接下来的一个工作,就是编写一个统一的访问接口。

[cpp] view plaincopy
  1. void play_movie_file(struct MoviePlay* pMoviePlay)
  2. {
  3. CommMoviePlay* pCommMoviePlay;
  4. assert(NULL != pMoviePlay);
  5. pCommMoviePlay = pMoviePlay->pCommMoviePlay;
  6. pCommMoviePlay->play(pCommMoviePlay->hFile);
  7. }

 最后的工作就是对不同的hFile进行play的实际操作,写简单一点就是,

[cpp] view plaincopy
  1. void play_avi_file(HANDLE hFile)
  2. {
  3. printf("play avi file!\n");
  4. }
  5. void play_rmvb_file(HANDLE hFile)
  6. {
  7. printf("play rmvb file!\n");
  8. }
  9. void play_mpeg_file(HANDLE hFile)
  10. {
  11. printf("play mpeg file!\n");
  12. }

C语言和设计模式(中介者模式)

中介者模式,听上去有一点陌生。但是,只要我给朋友们打个比方就明白了。早先自由恋爱没有现在那么普遍的时候,男女之间的相识还是需要通过媒婆之间才能相互认识。男孩对女方有什么要求,可以通过媒婆向女方提出来;当然,女方有什么要求也可以通过媒婆向男方提出来。所以,中介者模式在我看来,就是媒婆模式。

[cpp] view plaincopy
  1. typedef struct _Mediator
  2. {
  3. People* man;
  4. People* woman;
  5. }Mediator;

 上面的数据结构是给媒婆的,那么当然还有一个数据结构是给男方、女方的。

[cpp] view plaincopy
  1. typedef struct _People
  2. {
  3. Mediator* pMediator;
  4. void (*request)(struct _People* pPeople);
  5. void (*process)(struct _Peoplle* pPeople);
  6. }People;

 所以,这里我们看到的如果是男方的要求,那么这个要求应该女方去处理啊,怎么处理呢?

[cpp] view plaincopy
  1. void man_request(struct _People* pPeople)
  2. {
  3. assert(NULL != pPeople);
  4. pPeople->pMediator->woman->process(pPeople->pMediator->woman);
  5. }

 上面做的是男方向女方提出的要求,所以女方也可以向男方提要求了。毕竟男女平等嘛。

[cpp] view plaincopy
  1. void woman_request(struct _People* pPeople)
  2. {
  3. assert(NULL != pPeople);
  4. pPeople->pMediator->man->process(pPeople->pMediator->man);
  5. }

C语言和设计模式(建造者模式)

如果说前面的工厂模式是对接口进行抽象化处理,那么建造者模式更像是对流程本身的一种抽象化处理。这话怎么理解呢?大家可以听我慢慢到来。以前买电脑的时候,大家都喜欢自己组装机器。一方面可以满足自己的个性化需求,另外一方面也可以在价格上得到很多实惠。但是电脑是由很多部分组成的,每个厂家都只负责其中的一部分,而且相同的组件也有很多的品牌可以从中选择。这对于我们消费者来说当然非常有利,那么应该怎么设计呢?

[cpp] view plaincopy
  1. typedef struct _AssemblePersonalComputer
  2. {
  3. void (*assemble_cpu)();
  4. void (*assemble_memory)();
  5. void (*assemble_harddisk)();
  6. }AssemblePersonalComputer;

 对于一个希望配置intel cpu,samsung 内存、日立硬盘的朋友。他可以这么设计,

[cpp] view plaincopy
  1. void assemble_intel_cpu()
  2. {
  3. printf("intel cpu!\n");
  4. }
  5. void assemble_samsung_memory()
  6. {
  7. printf("samsung memory!\n");
  8. }
  9. void assemble_hitachi_harddisk()
  10. {
  11. printf("hitachi harddisk!\n");
  12. }

 而对于一个希望配置AMD cpu, kingston内存、西部数据硬盘的朋友。他又该怎么做呢?

[cpp] view plaincopy
  1. void assemble_amd_cpu()
  2. {
  3. printf("amd cpu!\n");
  4. }
  5. void assemble_kingston_memory()
  6. {
  7. printf("kingston memory!\n");
  8. }
  9. void assmeble_western_digital_harddisk()
  10. {
  11. printf("western digital harddisk!\n");
  12. }

C语言和设计模式(桥接模式)

在以往的软件开发过程中,我们总是强调模块之间要低耦合,模块本身要高内聚。那么,可以通过哪些设计模式来实现呢?桥接模式就是不错的一个选择。我们知道,在现实的软件开发过程当中,用户的要求是多种多样的。比如说,有这么一个饺子店吧。假设饺子店原来只卖肉馅的饺子,可是后来一些吃素的顾客说能不能做一些素的饺子。听到这些要求的老板自然不敢怠慢,所以也开始卖素饺子。之后,又有顾客提出,现在的肉馅饺子只有猪肉的,能不能做点牛肉、羊肉馅的饺子?一些只吃素的顾客也有意见了,他们建议能不能增加一些素馅饺子的品种,什么白菜馅的、韭菜馅的,都可以做一点。由此看来,顾客的要求是一层一层递增的。关键是我们如何把顾客的要求和我们的实现的接口进行有效地分离呢?

其实我们可以这么做,通常的产品还是按照共同的属性进行归类。

[cpp] view plaincopy
  1. typedef struct _MeatDumpling
  2. {
  3. void (*make)();
  4. }MeatDumpling;
  5. typedef struct _NormalDumpling
  6. {
  7. void (*make)();
  8. }NormalDumpling;

 上面只是对饺子进行归类。第一类是对肉馅饺子的归类,第二类是对素馅饺子的归类,这些地方都没有什么特别之处。那么,关键是我们怎么把它和顾客的要求联系在一起呢?

[cpp] view plaincopy
  1. typedef struct _DumplingReuqest
  2. {
  3. int type;
  4. void* pDumpling;
  5. }DumplingRequest;

 这里定义了一个饺子买卖的接口。它的特别支持就在于两个地方,第一是我们定义了饺子的类型type,这个type是可以随便扩充的;第二就是这里的pDumpling是一个void*指针,只有把它和具体的dumpling绑定才会衍生出具体的含义。

[cpp] view plaincopy
  1. void buy_dumpling(DumplingReuqest* pDumplingRequest)
  2. {
  3. assert(NULL != pDumplingRequest);
  4. if(MEAT_TYPE == pDumplingRequest->type)
  5. return (MeatDumpling*)(pDumplingRequest->pDumpling)->make();
  6. else
  7. return (NormalDumpling*)(pDumplingRequest->pDumpling)->make();
  8. }

C语言和设计模式(观察者模式)

观察者模式可能是我们在软件开发中使用得比较多的一种设计模式。为什么这么说?大家可以听我一一到来。我们知道,在windows的软件中,所有的界都是由窗口构成的。对话框是窗口,菜单是窗口,工具栏也是窗口。那么这些窗口,在很多情况下要对一些共有的信息进行处理。比如说,窗口的放大,窗口的减小等等。面对这一情况,观察者模式就是不错的一个选择。

首先,我们可以对这些共有的object进行提炼。

[cpp] view plaincopy
  1. typedef struct _Object
  2. {
  3. observer* pObserverList[MAX_BINDING_NUMBER];
  4. int number;
  5. void (*notify)(struct _Object* pObject);
  6. void (*add_observer)(observer* pObserver);
  7. void (*del_observer)(observer* pObserver);
  8. }Object;

 其实,我们需要定义的就是观察者本身了。就像我们前面说的一样,观察者可以是菜单、工具栏或者是子窗口等等。

[cpp] view plaincopy
  1. typedef struct _Observer
  2. {
  3. Object* pObject;
  4. void (*update)(struct _Observer* pObserver);
  5. }Observer;

 紧接着,我们要做的就是在Observer创建的时候,把observer自身绑定到Object上面。

[cpp] view plaincopy
  1. void bind_observer_to_object(Observer* pObserver, Object* pObject)
  2. {
  3. assert(NULL != pObserver && NULL != pObject);
  4. pObserver->pObject = pObject;
  5. pObject->add_observer(pObserver);
  6. }
  7. void unbind_observer_from_object(Observer* pObserver, Object* pObject)
  8. {
  9. assert(NULL != pObserver && NULL != pObject);
  10. pObject->del_observer(observer* pObserver);
  11. memset(pObserver, 0, sizeof(Observer));
  12. }

 既然Observer在创建的时候就把自己绑定在某一个具体的Object上面,那么Object发生改变的时候,统一更新操作就是一件很容易的事情了。

[cpp] view plaincopy
  1. void notify(struct _Object* pObject)
  2. {
  3. Obserer* pObserver;
  4. int index;
  5. assert(NULL != pObject);
  6. for(index = 0; index < pObject->number; index++)
  7. {
  8. pObserver = pObjecet->pObserverList[index];
  9. pObserver->update(pObserver);
  10. }
  11. }

 

C语言和设计模式(备忘录模式)

备忘录模式的起源来自于撤销的基本操作。有过word软件操作经验的朋友,应该基本上都使用过撤销的功能。举个例子,假设你不小心删除了好几个段落的文字,这时候你应该怎么办呢?其实要做的很简单,单击一些【撤销】就可以全部搞定了。撤销按钮给我们提供了一次反悔的机会。

既然是撤销,那么我们在进行某种动作的时候,就应该创建一个相应的撤销操作?这个撤销操作的相关定义可以是这样的。

[cpp] view plaincopy
  1. typedef struct _Action
  2. {
  3. int type;
  4. struct _Action* next;
  5. void* pData;
  6. void (*process)(void* pData);
  7. }Action;

 数据结构中定义了两个部分:撤销的数据、恢复的操作。那么这个撤销函数应该有一个创建的函数,还有一个恢复的函数。所以,作为撤销动作的管理者应该包括,

[cpp] view plaincopy
  1. typedef struct _Organizer
  2. {
  3. int number;
  4. Action* pActionHead;
  5. Action* (*create)();
  6. void (*restore)(struct _Organizer* pOrganizer);
  7. }Organizer;

 既然数据在创建和修改的过程中都会有相应的恢复操作,那么要是真正恢复原来的数据也就变得非常简单了。

[cpp] view plaincopy
  1. void restore(struct _Organizer* pOrganizer)
  2. {
  3. Action* pHead;
  4. assert(NULL != pOrganizer);
  5. pHead = pOrganizer->pActionHead;
  6. pHead->process(pHead->pData);
  7. pOrganizer->pActionHead = pHead->next;
  8. pOrganizer->number --;
  9. free(pHead);
  10. return;
  11. }

C语言和设计模式(解释器模式)

解释器模式虽然听上去有些费解,但是如果用示例说明一下就不难理解了。我们知道在C语言中,关于变量的定义是这样的:一个不以数字开始的由字母、数字和下划线构成的字符串。这种形式的表达式可以用状态自动机解决,当然也可以用解释器的方式解决。

[cpp] view plaincopy
  1. typedef struct _Interpret
  2. {
  3. int type;
  4. void* (*process)(void* pData,int* type, int* result);
  5. }Interpret;

 上面的数据结构比较简单,但是很能说明问题。就拿变量来说吧,这里就可以定义成字母的解释器、数字解释器、下划线解释器三种形式。所以,我们可以进一步定义一下process的相关函数。

[cpp] view plaincopy
  1. #define DIGITAL_TYPE 1
  2. #define LETTER_TYPE 2
  3. #define BOTTOM_LINE 3
  4. void* digital_process(void* pData,int* type, int* result)
  5. {
  6. UINT8* str;
  7. assert(NULL != pData && NULL != type && NULL != result);
  8. str = (UNT8*)pData;
  9. while (*str >= '0' && *str <= '9')
  10. {
  11. str ++;
  12. }
  13. if(*str == '\0')
  14. {
  15. *result = TRUE;
  16. return NULL;
  17. }
  18. if(*str == '_')
  19. {
  20. *result = TRUE;
  21. *type = BOTTOM_TYPE;
  22. return str;
  23. }
  24. if(*str >= 'a' && *str <= 'z' || *str >='A' && *str <= 'Z')
  25. {
  26. *result = TRUE;
  27. *type = LETTER_TYPE;
  28. return str;
  29. }
  30. *result = FALSE;
  31. return NULL;
  32. }
  33. void* letter_process(void* pData,int* type, int* result)
  34. {
  35. UINT8* str;
  36. assert(NULL != pData && NULL != type && NULL != result);
  37. str = (UNT8*)pData;
  38. while (*str >= 'a' && *str <='z' || *str >= 'A' && *str <='Z')
  39. {
  40. str ++;
  41. }
  42. if(*str == '\0')
  43. {
  44. *result = TRUE;
  45. return NULL;
  46. }
  47. if(*str == '_')
  48. {
  49. *result = TRUE;
  50. *type = BOTTOM_TYPE;
  51. return str;
  52. }
  53. if(*str >= '0' && *str <='9')
  54. {
  55. *result = TRUE;
  56. *type = DIGITAL_TYPE;
  57. return str;
  58. }
  59. *result = FALSE;
  60. return NULL;
  61. }
  62. void* bottom_process(void* pData,int* type, int* result)
  63. {
  64. UINT8* str;
  65. assert(NULL != pData && NULL != type && NULL != result);
  66. str = (UNT8*)pData;
  67. while ('_' == *str )
  68. {
  69. str ++;
  70. }
  71. if(*str == '\0')
  72. {
  73. *result = TRUE;
  74. return NULL;
  75. }
  76. if(*str >= 'a' && *str <='z' || *str >= 'A' && *str <='Z')
  77. {
  78. *result = TRUE;
  79. *type = LETTER_TYPE;
  80. return str;
  81. }
  82. if(*str >= '0' && *str <= '9')
  83. {
  84. *result = TRUE;
  85. *type = DIGITAL_TYPE;
  86. return str;
  87. }
  88. *result = FALSE;
  89. return NULL;
  90. }

C语言和设计模式(命令模式)

命令模式的目的主要是为了把命令者和执行者分开。老规矩,举个范例吧。假设李老板是一家公司的头儿,他现在让他的秘书王小姐去送一封信。王小姐当然不会自己亲自把信送到目的地,她会把信交给邮局来完成整个投递的全过程。现在,我们就对投递者、命令、发令者分别作出定义。

首先定义post的相关数据。

[cpp] view plaincopy
  1. typedef struct _Post
  2. {
  3. void (*do)(struct _Post* pPost);
  4. }Post;

 Post完成了实际的投递工作,那么命令呢?

[cpp] view plaincopy
  1. typedef struct _Command
  2. {
  3. void* pData;
  4. void (*exe)(struct _Command* pCommand);
  5. }Command;
  6. void post_exe(struct _Command* pCommand)
  7. {
  8. assert(NULL != pCommand);
  9. (Post*)(pCommand->pData)->do((Post*)(pCommand->pData));
  10. return;
  11. }

 我们看到了Post、Command的操作,那么剩下的就是boss的定义了。

[cpp] view plaincopy
  1. typedef struct _Boss
  2. {
  3. Command* pCommand;
  4. void (*call)(struct _Boss* pBoss);
  5. }Boss;
  6. void boss_call(struct _Boss* pBoss)
  7. {
  8. assert(NULL != pBoss);
  9. pBoss->pCommand->exe(pBoss->pCommand);
  10. return;
  11. }

C语言和设计模式(状态模式)

状态模式是协议交互中使用得比较多的模式。比如说,在不同的协议中,都会存在启动、保持、中止等基本状态。那么怎么灵活地转变这些状态就是我们需要考虑的事情。假设现在有一个state,

[cpp] view plaincopy
  1. typdef struct _State
  2. {
  3. void (*process)();
  4. struct _Sate* (*change_state)();
  5. }State;

 说明一下,这里定义了两个变量,分别process函数和change_state函数。其中proces函数就是普通的数据操作,

[cpp] view plaincopy
  1. void normal_process()
  2. {
  3. printf("normal process!\n");
  4. }

 change_state函数本质上就是确定下一个状态是什么。

[cpp] view plaincopy
  1. struct _State* change_state()
  2. {
  3. STATE* pNextState = NULL;
  4. pNextState = (struct _State*)malloc(sizeof(struct _State));
  5. assert(NULL != pNextState);
  6. pNextState ->process = next_process;
  7. pNextState ->change_state = next_change_state;
  8. return pNextState;
  9. }

 所以,在context中,应该有一个state变量,还应该有一个state变换函数。

[cpp] view plaincopy
  1. typedef struct _Context
  2. {
  3. State* pState;
  4. void (*change)(struct _Context* pContext);
  5. }Context;
  6. void context_change(struct _Context* pContext)
  7. {
  8. State* pPre;
  9. assert(NULL != pContext);
  10. pPre = pContext->pState;
  11. pContext->pState = pPre->changeState();
  12. free(pPre);
  13. return;
  14. }

C语言和设计模式(访问者模式)

不知不觉当中,我们就到了最后一种设计模式,即访问者模式。访问者模式,听上去复杂一些。但是,这种模式用简单的一句话说,就是不同的人对不同的事物有不同的感觉。比如说吧,豆腐可以做成麻辣豆腐,也可以做成臭豆腐。可是,不同的地方的人未必都喜欢这两种豆腐。四川的朋友可能更喜欢辣豆腐,江浙的人就可能对臭豆腐更喜欢一些。那么,这种情况应该怎么用设计模式表达呢?

[cpp] view plaincopy
  1. typedef struct _Tofu
  2. {
  3. int type;
  4. void (*eat) (struct _Visitor* pVisitor,struct _Tofu* pTofu);
  5. }Tofu;
  6. typedef struct _Visitor
  7. {
  8. int region;
  9. void (*process)(struct _Tofu* pTofu,struct _Visitor* pVisitor);
  10. }Visitor;

 就是这样一个豆腐,eat的时候就要做不同的判断了。

[cpp] view plaincopy
  1. void eat(struct _Visitor* pVisitor,struct _Tofu* pTofu)
  2. {
  3. assert(NULL != pVisitor && NULL != pTofu);
  4. pVisitor->process(pTofu, pVisitor);
  5. }

 既然eat的操作最后还是靠不同的visitor来处理了,那么下面就该定义process函数了。

[cpp] view plaincopy
  1. void process(struct _Tofu* pTofu,struct _Visitor* pVisitor)
  2. {
  3. assert(NULL != pTofu && NULL != pVisitor);
  4. if(pTofu->type == SPICY_FOOD && pVisitor->region == WEST ||
  5. pTofu->type == STRONG_SMELL_FOOD && pVisitor->region == EAST)
  6. {
  7. printf("I like this food!\n");
  8. return;
  9. }
  10. printf("I hate this food!\n");
  11. }

C语言和设计模式(继承、封装、多态)

记得还在我们大学C++第一门课的时候,老师就告诉我们说,C++是一门面向对象的语言。C++有三个最重要的特点,即继承、封装、多态。等到后来随着编码的增多和工作经验的积累,我也慢慢明白了面向对象的含义。可是,等我工作以后,使用的编程语言更多的是C语言,这时候我又想能不能把C语言变成面向对象的语言呢?等到后来通过思考和实践,我发现其实C语言也是可以面向对象的,也是可以应用设计模式的,关键就在于如何实现面向对象语言的三个重要属性。

(1)继承性

[cpp] view plaincopy
  1. typedef struct _parent
  2. {
  3. int data_parent;
  4. }Parent;
  5. typedef struct _Child
  6. {
  7. struct _parent parent;
  8. int data_child;
  9. }Child;

 在设计C语言继承性的时候,我们需要做的就是把基础数据放在继承的结构的首位置即可。这样,不管是数据的访问、数据的强转、数据的访问都不会有什么问题。

(2)封装性

[cpp] view plaincopy
  1. struct _Data;
  2. typedef void (*process)(struct _Data* pData);
  3. typedef struct _Data
  4. {
  5. int value;
  6. process pProcess;
  7. }Data;

 封装性的意义在于,函数和数据是绑在一起的,数据和数据是绑在一起的。这样,我们就可以通过简单的一个结构指针访问到所有的数据,遍历所有的函数。封装性,这是类拥有的属性,当然也是数据结构体拥有的属性。


(3)多态

[cpp] view plaincopy
  1. typedef struct _Play
  2. {
  3. void* pData;
  4. void (*start_play)(struct _Play* pPlay);
  5. }Play;

 多态,就是说用同一的接口代码处理不同的数据。比如说,这里的Play结构就是一个通用的数据结构,我们也不清楚pData是什么数据,start_play是什么处理函数?但是,我们处理的时候只要调用pPlay->start_play(pPlay)就可以了。剩下来的事情我们不需要管,因为不同的接口会有不同的函数去处理,我们只要学会调用就可以了。


转载地址:http://blog.csdn.net/mac_cm/article/details/7361318

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. BETA冲刺(1/7)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记录: 暂无 接下来的计划 继续学习java 继续学习Android Studio 继续增加功能 还剩…...

    2024/5/6 9:50:29
  2. 两年软件开发工作总结及未来展望

    光阴似箭,日月如梭,转眼之间,我毕业工作都将近两年了。回首往事,我最大的感触就是:时间都去哪儿了呢? 最近,我看到CSDN举办了一个叫做畅想“我的IT成长路”的活动,觉得挺有意思的。作为广大的软件开发工程师中的一员,我和大部分人走过的路都是一样的。我们从学校…...

    2024/5/6 9:19:28
  3. 曾经最强浏览器王者归来!火狐量子 vs 谷歌浏览器:哪一个更快呢?

    来自:开源中国社区 链接:www.oschina.net/news/90739/firefox-quantum-vs-google-chrome 原文:http://mashable.com/2017/11/15/google-chrome-vs-firefox-quantum/#Ws_PzIW_Baqw为了扭转颓势,Mozilla发力研发,终于Firefox 57发布,使用了全新的引擎,性能大增,官方特地正…...

    2024/4/19 21:47:59
  4. 前端简单学习

    前端简单学习一、HTML二、CSS三、JS 一、HTML概念:是最基础的网页开发语言 含义:Hyper Text Markup Language 超文本标记语言超文本:用超链接的方法,将各种不同空间的文字信息组织在一起的网状文本 标记语言:由标签构成的语言。<标签名称> 如 html,xml。标记语言不是…...

    2024/4/18 5:21:31
  5. 嵌入式编程中的多任务与单任务,以及单任务的软件设计流程

    多任务与单任务 所谓"单任务系统"是指该系统不能支持多任务并发操作,宏观串行地执行一个任务。而多任务系统则可以宏观并行(微观上可能串行)地"同时"执行多个任务。 多任务的并发执行通常依赖于一个多任务操作系统(OS),多任务OS的核心是系统调度器,…...

    2024/5/6 7:48:28
  6. 贪心算法 做题总结(待续!)

    贪心算法做题 题解Crossing RiverGame PredictionBest Cow LineSarumans ArmyFence Repair今年暑假不AC悼念512汶川大地震遇难同胞——老人是真饿了Filthy Rich很难受贪心算法还是掌握不牢固——————————加油吧!!Crossing River 题目:: A group of N people wishe…...

    2024/4/18 5:25:44
  7. Beta 冲刺(2/7)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记录: 暂无 接下来的计划 继续学习java 继续学习Android Studio 继续增加功能 还剩…...

    2024/5/6 17:16:02
  8. 浏览器 发展历史

    浏览器发展 作为访问internet的工具,浏览器已经成为我们日常生活中必不可少的上网工具了,它能让你加入全球的网络,通过一个窗口就能够连接世界。当你用浏览器时有没有想过浏览器的发展历史?面对市场上繁多的浏览器你又为什么偏爱你现在用的? chrome来自Net Market Share的…...

    2024/4/18 10:42:18
  9. GitHub 夜间阅读模式 可自行DIY

    最近再github上阅读文章,屏幕属实太亮了,晚上更是特刺激眼睛,做了个夜间模式脚本,样式可以直接在 createCss 方法中修改 自行DIY使用方法1.油猴加载 (没有梯子,自行寻找资源)2.在控制台 复制粘贴 输入屏幕右下角有个按钮,通过按钮控制代码:(function() {use strict;c…...

    2024/4/20 11:44:18
  10. 微信小程序云开发js抓取网页内容

    微信小程序云开发js抓取网页内容 最近在研究微信小程序的云开发功能。云开发最大的好处就是不需要前端搭建服务器,可以使用云端能力,从零开始写出来一个能上线的微信小程序,避免了买服务器的开销,对于个人来尝试练手微信小程序从前端到后台的开发,还是一个不错的选择。可以…...

    2024/4/12 1:08:27
  11. 音频透传背后的技术实现

    现在市面上流行的电视盒大部分都是Android,“音频透传”是一个经常见到的词,那到底什么是音频透传、音频透传背后的技术实现到底如何,引起了我的兴趣,因此花了点时间研究了一下。由于是针对全志H8的电视盒方案进行分析,因此分析的结果不具有普遍性,可能其它的方案在技术实…...

    2024/4/18 5:40:37
  12. 编程应该纳入中小学吗?

    原创文章,欢迎转载。转载请注明:转载自IT人故事会,谢谢! 原文链接地址:编程应该纳入中小学吗?实施全民智能教育项目,在中小学阶段设置人工智能相关课程,逐步推广编程教育,鼓励社会力量参与寓教于乐的编程教学软件、游戏的开发和推广。信息技术加入中小学学习内容,并成为…...

    2024/4/12 1:08:33
  13. 前端人员必须了解的各种浏览器

    做为一个前端人员最烦恼的就是各种浏览器的兼容性问题。从微软的IE6、IE7、IE8到火狐公司的FIREFOX以及其他各公司的浏览器多多少少存在一定的兼容性问题。每次你在写HTML的时候是否会感到相当苦恼。以下我们简单介绍下目前世界流行的各主流浏览器。1、IE浏览器这款浏览器是目前…...

    2024/4/18 16:27:31
  14. 物联网的嵌入式编程

    嵌入式编程在使设备满足人们的需求方面具有悠久的历史。但是,它在很大程度上仍然被应用程序编程所掩盖。当应用程序程序员采用相对高级的面向对象的语言(如C ++或Java)或图形化应用程序开发环境(如MATLAB)时,嵌入式程序员依然在使用C语言编程。它们总是被应用程序程序员所…...

    2024/4/18 5:29:26
  15. 微信小程序调用微信支付接口

    前言:应项目要求,需要使用微信小程序做支付,写完后告知手续费太高方案不予通过(宝宝心里苦,但宝宝不说)。此次开发在因站在巨人的肩膀上顺利完成。微信支付文档传送门:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_31.开发工具:Eclipse+Tomcat+…...

    2024/4/20 12:27:45
  16. 0day安全:软件漏洞分析技术(第2版)

    安全技术大系 0day安全:软件漏洞分析技术(第2版) 王清主编ISBN978-7-121-13396-1 2011年6月出版定价:85.00元16开780页内 容 简 介本书分为5篇33章,系统、全面地介绍了Windows平台缓冲区溢出漏洞的分析、检测与防护。第一篇为漏洞exploit的基础理论和初级技术,可以引领读…...

    2024/4/25 20:15:45
  17. 你想要什么样的“呆呆”?

    关于“呆呆”这样一个情侣聊天软件到底应该有什么样的feature,丰富强大的功能和良好愉悦的用户体验之间到底应该怎么平衡,老实说我们真的都没有底。也许用户的需求才是我们继续工作的方向,所以这里把用户调查时得到的反馈放上来(在此向zi ling同学表示最诚挚的感谢),也欢…...

    2024/4/18 5:25:38
  18. 星际联盟联合创始人&CTO带来解答 | 火热的Filecoin,未来会走向何方

    近期关于Filecoin的热点话题不断。6月29日,星际联盟联合创始人&CTO毛必盛先生受邀参加支点访谈,以“Filecoin是否能买,以哪种形式购买,如何避免踏坑”等主题展开讨论,解读火热的Filecoin,未来会走向何处。 以下是整理星际联盟联合创始人&CTO毛必盛先生的发言,帮…...

    2024/4/18 5:27:51
  19. 遨游浏览器“广告快进”产品思考和技术初探

    遨游浏览器“广告快进”产品思考和技术初探作者:magictong前言 这个功能个人觉得在产品侧的创新要大于在技术侧创新,因此花了较大篇幅来讲解该对该功能的产品体验和产品思考,而把技术分析放在了最后面。背景 视频网站播放视频时,前面长达30秒,甚至60秒的视频广告一直是用户…...

    2024/4/19 9:58:13
  20. Beta 冲刺(1/7)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记录: 暂无 接下来的计划 继续学习java 继续学习Android Studio 继续增加功能 还剩…...

    2024/5/5 6:17:32

最新文章

  1. 高中数学-三角函数之常见题型总结

    相关公式 新教材&#xff0c;取消了和差化积与积化和差的三角函数题目 例题1 解析 根据条件tanθ -2&#xff0c;我们应该就要想到&#xff0c;把待求式的角向θ靠拢 所以要想到如何降角&#xff0c;将2θ化成θ&#xff0c;那么&#xff0c;想到的公式就是&#xff1a;正弦…...

    2024/5/6 18:06:40
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/6 9:38:23
  3. Stable Diffusion的界面参数详解

    Stable Diffusion作为一款强大的文本到图像生成模型,其界面参数是用户与模型进行交互的重要桥梁。这些参数不仅影响着模型的生成效果,还能够帮助用户更加精准地控制生成图像的风格、内容等。本文将详细介绍Stable Diffusion的界面参数,帮助用户更好地理解和应用这一工具。 …...

    2024/5/5 23:17:17
  4. LeetCode-200. 岛屿数量【深度优先搜索 广度优先搜索 并查集 数组 矩阵】

    LeetCode-200. 岛屿数量【深度优先搜索 广度优先搜索 并查集 数组 矩阵】 题目描述&#xff1a;解题思路一&#xff1a;bfs&#xff0c;主要思想都是遇到一个没有visited过的"陆地"先result 1&#xff0c;然后用深搜或者广搜将这片"陆地"全部做上visited标…...

    2024/5/5 15:07:34
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/6 9:21:00
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; 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
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在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