设计模式


设计模式

单例模式

保证类的实例化对象只有一个,并且提供一个访问它的全局访问点。

应用场景:

  • 标识文件系统的类,一个操作系统一定是只有一个文件系统,因此文件系统的类的实例有且仅有一个。
  • 打印机打印程序的实例,一台计算机可以连接好几台打印机,但是计算机上的打印程序只有一个,就可以通过单例模式来避免两个打印作业同时输出到打印机。

实现方式:

单例模式可以通过全局或者静态变量的形式实现,这样比较简单,但是这样会影响封装性,难以保证别的代码不会对全局变量造成影响。

默认的构造函数,拷贝构造函数,赋值构造函数声明为私有的,这样进制在类的外部创建该对象。

全局访问点也要定义为静态类型的成员函数,没有参数,返回该类的指针类型。因此使用实例化对象的时候是通过类直接调用该函数,并不是先创建一个该类的对象,通过对象调用

不安全的实现方式:

原因:考虑当两个线程同时调用 getInstance 方法,并且同时检测到instance是NULL,两个线程会同时实例化对象,不符合单例模式的要求。

class Singleton{
private:
    static Singleton * instance;
    Singleton(){}
    Singleton(const Singleton &tmp){}
    Singleton & operator = (const Singleton &tmp){}
public:
    static Singleton *getInstance(){
        if(instance == NULL){
            instance = new Singleton();
        }
        return instance;
    }
};
Singleton *Singleton :: instance = NULL;

分类:h

  • 懒汉模式:直到第一次用到类的实例时才会去实例化,上面是懒汉实现。
  • 饿汉模式:类定义的时候就实例化

线程安全的懒汉模式实现:

方法:加锁

存在的问题:每次判断实例对象是否为NULL,都要被锁定,如果是多线程,就会造成大量线程阻塞。

class Singleton{
private:
    static pthread_mutex_t mutex;
    static Singleton *instance;
    Singleton(){
        pthread_mutex_init(&mutex,NULL);
    }
    Singleton(const Singleton &tmp);
    Singleton & operator = (const Singleton &tmp);
public:
    static Singleton * getInstance(){
        pthread_mutex_lock(&mutex);
        if(instance == NULL){
            instance = new Singleton();
        }
        pthread_mutex_unlock(&mutex);
        return instance;
    }
};
Singleton *Singleton ::instance = NULL;
pthread_mutex_t Singleton::mutex;

方法:内部静态变量,在全局访问点getInstance中定义静态实例

class Singleton{
private:
    static pthread_mutex_t mutex;
    Singleton(){
        pthread_mutex_init(&mutex,NULL);
    }
    Singleton(const Singleton &temp);
    Singleton & operator = (const Singleton &temp);
public:
    static Singleton * getInstance(){
        static Singleton instance;
        return instance;
    }
};
pthread_mutex_t Singleton::mutex;

饿汉模式的实现:

饿汉模式本身就是线程安全的,不用加锁。

class Singleton{
private:
    static Singleton *instance;
    Singleton(const Singleton &tmp);
    Singleton & operator = (const Singleton &tmp);
protected:
    Singleton(){}
public:
    static Singleton * getInstance(){
        return instance;
    }
};
Singleton *Singleton ::instance = new Singleton();

工厂模式

工厂模式:包括简单工厂模式,抽象工厂模式,工厂方法模式

简单工厂模式:主要用于创建对象。用一个工厂来根据输入的条件产生不同的类,然后根据不同的类的虚函数得到不同的结果。

工厂方法模式:修正了简单工厂模式不遵守开放封闭原则。把选择判断移到了客户端去实现,如果想添加新功能就不用修改原来的类,直接修改客户端即可

抽象工厂模式:定义了一个创建一系列相关或相互依赖的接口,而无需指定他们的具体类。

1.简单工厂模式

主要用于创建对象。用一个工厂来根据输入的条件产生不同的类,然后根据不同类的虚函数得到不同的结果。

应用场景:

适用于针对不同情况创建不同类时,只需传入工厂类的参数即可,无需了解具体实现方法。例如计算器中对于同样的输入,执行不同的操作:加减乘除。

实现方式:

#include<iostream>
#include<vector>
using namespace std;

//here is the product class
class Operation_Pos(){
public:
    int var1,var2;
    virtual double GetResult(){
        double res = 0;
        return res;
    }
};
class Add_Operation_Pos : public Operation_Pos{
public:
    virtual double GetResult(){
        return var1 + var2;
    }
    
};
class Sub_Operation_Pos : public Operation_Pos{
public:
    virtual double GetResult(){
        return var1 - var2;
    }
    
};
class Mul_Operation_Pos : public Operation_Pos{
public:
    virtual double GetResult(){
        return var1 * var2;
    }
    
};
class Div_Operation_Pos : public Operation_Pos{
public:
    virtual double GetResult(){
        return var1 / var2;
    }
};

//here is factory class
class Factory{
public:
    static Operation_Pos *CreateProduct(char op){
        switch(op){
            case '+':
                return new Add_Operation_Pos();
            case '-':
                return new Sub_Operation_Pos();                   case '*':
                return new Mul_Operation_Pos();                   case '/':
                return new Div_Operation_Pos();
            default:
                return new Add_Operation_Pos();
        }
    }
};
int main(){
    int a,b;
    cin>>a>>b;
    Operation_Pos *p = Factory::CreateProduct('+');
    p->var1 = a;
    p->var2 = b;
    cout<<p->GetResult()<<endl;
    
    p = Factory::CreateProduct('*');
    p->var1 = a;
    p->var2 = b;
    cout<<p->GetResult()<<endl;
    return 0;
}

2.工厂方法模式

修正了简单工厂模式中不遵守开放封闭原则。把选择判断移到了客户端去实现,如果想添加新功能就不用修改原来的类,直接修改客户端即可。

应用场景:

一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要直到产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,客户端需要知道创建具体产品的工厂类

一个类通过其派生类来指定创建哪个对象:在工厂模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其派生类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,程序运行时,派生类对象将覆盖父类对象,从而使系统更容易扩展。

将创建对象的任务委托给多个工厂派生类中的某一个,客户端在使用时可以无需关心是哪个工厂派生类创建产品派生类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库。

#include<iostream>
#include<vector>
using namespace std;

 //here is the product class
class Operation_Pos{
public:
    int var1,var2;
    virtual double GetResult(){
        double res = 0;
        return res;
    }
};

class Add_Operation_Pos : public Operation_Pos
{
public:
     virtual double GetResult(){
         return var1 + var2;
     }
};

class Sub_Operation_Pos : public Operation_Pos
{
public:
     virtual double GetResult(){
         return var1 - var2;
     }
};

class Mul_Operation_Pos : public Operation_Pos
{
public:
     virtual double GetResult(){
         return var1 * var2;
     }
};

class Div_Operation_Pos : public Operation_Pos
{
public:
     virtual double GetResult(){
         return var1 / var2;
     }
};

class Factory{
public:
    virtual Operation_Pos *CreateProduct() = 0;
};

class Add_Factory : public Factory{
public:
    Operation_Pos *CreateProduct(){
        return new Add_Operation_Pos();
    }
};

class Sub_Factory : public Factory{
public:
    Operation_Pos *CreateProduct(){
        return new Sub_Operation_Pos();
    }
};

class Mul_Factory : public Factory{
public:
    Operation_Pos *CreateProduct(){
        return new Mul_Operation_Pos();
    }
};

class Div_Factory : public Factory{
public:
    Operation_Pos *CreateProduct(){
        return new Div_Operation_Pos();
    }
};

int main(){
    int a,b;
    cin>>a>>b;
    //
    Add_Factory *p_fac = new Add_Factory();
    //
    Operation_Pos *p_pro = p_fac->CreateProduct();
    p_pro->var1 = a;
    p_pro->var2 = b;
    cout<<p_pro->GetResult()<<endl;
    //
    Mul_Factory *p_fac1 = new Mul_Factory();
    //
    Operation_Pos *p_pro1 = p_fac1->CreateProduct();
    p_pro1->var1 = a;
    p_pro1->var2 = b;
    cout << p_pro1->GetResult() << endl;
    return 0;
}

3.抽象工厂模式

定义了一个创建一系列相关或相互依赖的接口,而无需指定他们的具体类

应用场景:

  • 一个系统不应当依赖于产品类实例如何被创建,组合和表达的细节,这对于所有类型的工厂模式都是重要的。
  • 系统中有多于一个产品族,而每次只是用其中某一个产品族。
  • 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
  • 产品等级结构稳定,设计完成后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。

实现方式:

#include<iostream>
#include<vector>
using namespace std;

 //here is the product class
class Operation_Pos{
public:
    int var1,var2;
    virtual double GetResult(){
        double res = 0;
        return res;
    }
};

class Add_Operation_Pos : public Operation_Pos
{
public:
     virtual double GetResult(){
         return var1 + var2;
     }
};

class Sub_Operation_Pos : public Operation_Pos
{
public:
     virtual double GetResult(){
         return var1 - var2;
     }
};

class Mul_Operation_Pos : public Operation_Pos
{
public:
     virtual double GetResult(){
         return var1 * var2;
     }
};

class Div_Operation_Pos : public Operation_Pos
{
public:
     virtual double GetResult(){
         return var1 / var2;
     }
};

/*............................................*/
class Operation_Neg{
public:
    int var1,var2;
    virtual double GetResult(){
        double res = 0;
        return res;
    }
}

class Add_Operation_Neg : public Operation_Neg{
public:
    virtual double GetResult(){
        return -(var1 + var2);
    }
};

class Sub_Operation_Neg:public Operation_Neg{
public:
    virtual double GetResult(){
        return -(var1-var2);
    }
};

class Mul_Operation_Neg:public Operation_Neg{
public:
    virtual double GetResult(){
        return -(var1*var2);
    }
};

class Div_Operation_Neg:public Operation_Neg{
public:
    virtual double GetResult(){
        return -(var1/var2);
    }
};

//.................................................

//here is the factory class

class Factory{
public:
    virtual Operation_Pos *CreateProduct_Pos() = 0;
    virtual Operation_Neg *CreateProduct_Neg() = 0;
};

class Add_Factory : public Factory{
public:
    Operation_Pos *CreateProduct_Pos(){
        return new Add_Operation_Pos();
    }
    Operation_Pos *CreateProduct_Neg(){
        return new Add_Operation_Neg();
    }
};

class Sub_Factory : public Factory{
public:
    Operation_Pos *CreateProduct(){
        return new Sub_Operation_Pos();
    }
    Operation_Pos *CreateProduct_Neg(){
        return new Sub_Operation_Neg();
    }
};

class Mul_Factory : public Factory{
public:
    Operation_Pos *CreateProduct(){
        return new Mul_Operation_Pos();
    }
    Operation_Pos *CreateProduct_Neg(){
        return new Mul_Operation_Neg();
    }
};

class Div_Factory : public Factory{
public:
    Operation_Pos *CreateProduct(){
        return new Div_Operation_Pos();
    }
    Operation_Pos *CreateProduct_Neg(){
        return new Div_Operation_Neg();
    }
};

int main(){
    int a,b;
    cin>>a>>b;
    //
    Add_Factory *p_fac = new Add_Factory();
    //
    Operation_Pos *p_pro = p_fac->CreateProduct_Pos();
    p_pro->var1 = a;
    p_pro->var2 = b;
    cout<<p_pro->GetResult()<<endl;
    //
    Add_Factory * p_fac1  = new Add_Factory();
    //
    Operation_Neg *p_pro1 = p_fac1->CreateProduct_Neg();
    p_pro1->var1 = a;
    p_pro1->var2 = b;
    cout << p_pro1->GetResult() << endl;
    return 0;
}

观察者模式

观察者模式

定义一种一(被观察类)对多(观察类)的关系,让多个观察对象同时监听一个被观察对象,被观察对象状态发生变化时,会通知所有的观察对象,使他们能够更新自己的状态。

观察者模式存在两种角色:

  • 观察者:内部包含被观察者对象,当被观察者对象的状态发生变化时,更新自己的状态。(接收通知更新状态)
  • 被观察者:内部包含了所有观察者对象,当状态发生变化时通知所有的观察者更新自己的状态。(发送通知)

应用场景:

当一个对象的改变需要同时改变其他对象,且不知道具体有多少对象有待改变时,应该考虑使用观察者模式。

一个抽象模型有两个方面,其中一个方面依赖于另一个方面,这时可以用观察者模式将这两者封装在独立的对象中使他们各自独立地改变和复用。

#include<iostream>
#include<list>
#include<string>
using namespace std;
class Subject;
//观察者基类 (内部实例化了被观察者的对象Sub)
class Observer{
protected:
    string name;
    Subject *sub;
public:
    Observer(string name,Subject *sub){
        this->name = name;
        this->sub = sub;
    }
    virtual void update() = 0;
};

class StockObserver : public Observer{
public:
    StockObserver(string name,Subject *sub):Observer(name,sub){
        
    }
    void update();
};

class NBAObserver : public Observer{
public:
    NBAObserver(string name,Subject *sub):Observer(name,sub){
        
    }
    void update();
};

//被观察者基类 (内部存放了所有的观察者对象,以便状态发生变化时,给观察者发通知)

class Subject{
protected:
    list<Observer *>observers;
public:
    string action ; //被观察者对象的状态
    virtual void attach(Observer *) = 0;
    virtual void detach(Observer *) = 0;
    virtual void notify() = 0;
};

class Secretary : public Subject{
    void attach(Observer *observer){
        observers.push_back(observer);
    }
    void detach(Observer * observer){
        list<Observer *>::iterator iter = observers.begin();
        while(iter!=observers.end()){
            if((*iter) == observer){
                observers.erase(iter);
                return;
            } 
            ++iter;
        }
    }
    
    void notify(){
        list<Observer *>::iterator iter =observers.begin();
        while(iter!=observers.end()){
            (*iter)->update();
            ++iter;
        }
    }
};

void StockObserver :: update(){
    cout<<name <<"收到消息:"<<sub->action<<endl;
    if(sub->action == "梁所长来了!"){
        cout<<"我马上关闭股票装作很认真的样子"<<endl;
    }
}

void NBAObserver :: update(){
    cout<<name<<"收到消息:"sub->action<<endl;
    if(sub->action == "梁所长来了!"){
        cout<<"我马上关闭NBA,装作认真工作的样子"<<endl;
    }
    
}

int main(){
    Subject *dwq = new Secretary();
    Observer *xs = new NBAObserver("xiaoshuai",dwq);
    Observer *zy = new NBAObserver("zouyue",dwq);
    Observer *lm = new NBAObserver("limin",dwq);
    dwq->attach(xs);
    dwq->attach(zy);
    dwq->attach(lm);
    
    dwq->action == "去吃饭了!";
    dwq->notify();
    cout<< endl;
    dwq->action = "梁所长来了!";
    dwq.notify();
    return 0;

}

输出:

xiaoshuai收到消息
zouyue收到消息
limin收到消息

xiaoshuai收到消息梁所长来了!
我马上关闭NBA,装作认真工作的样子
zouyue收到消息梁所长来了!
我马上关闭NBA,装作认真工作的样子
limin收到消息梁所长来了!
我马上关闭NBA,装作认真工作的样子

面向对象

面向对象:对象是指具体的某一个事物,这些事物的抽象就是类,类中包含数据(成员变量)和动作(成员方法)。

面向对象的三大特性

封装:将具体的实现过程和数据封装成一个函数,只能通过接口进行访问,降低耦合性。——>代码之间应该相互独立 ,降低依赖关系易于扩展。

继承:**子类继承父类的特征和行为**,子类有父类的非private方法或成员变量,子类可以对父类的方法进行重写增强了类之间的耦合性,但是当父类的成员变量,成员函数,或者类本身被final关键字修饰的时候,修饰的类不能继承,修饰的成员不能重写或修改。

多态:多态就是不同继承类的对象。对同一消息做出不同的响应,基类的指针指向或绑定到派生类的对象,使得基类指针呈现不同的表现方式。

重载,重写,隐藏的区别

概念解释:·

重载**:是指同一可访问区内被声明几个具有不同参数列(参数的类型,个数,顺序)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数的返回类型。**

class A{
public:
    void fun(int tmp);
    void fun(float tmp);
    void fun(int tmp,float tmp1);
    void fun(float tmp,int tmp1);
    int fun(int tmp);   // error 注意重载不关心函数返回类型
    
};

隐藏**:是指派生类的函数屏蔽了与其同名的基类函数。只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。——–>**在派生类中构建的对象无法调用基类中的同名函数。

#include<iostream>
using namespace std;
class Base{
public:
    void fun(int tmp,float tmp1){
        cout<<"Base::fun(int tmp,float tmp1)"<<endl;
    }   
};

class Derive: public Base{
public:
    void fun(int tmp){
        cout<<"Derive :: fun(int tmp)"<<endl; //隐藏基类中的同名函数
    }
};

int main(){
    Derive ex;
    ex.fun(1);
    ex.fun(1,0.01); //error :candidate expects 1 arguement 2 provided
    return 0;

说明:上述代码中ex.fun(1,0.01) ;出现错误,说明派生类中将基类的同名函数隐藏了。若是想调用基类中的同名函数,可以加上类型名指明 ex.Base::fun(1,0.01),这样就可以调用基类中的同名函数。

**重写(覆盖)**:是指派生类中存在重写定义的函数,函数名,参数列表,返回值类型都必须同基类中被重写的函数一致,只有函数体不同。派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写函数必须有virtual修饰。

#include<iostream>
using namespace std;
class Base{
public:
    virtual void fun(int tmp){
        cout << "Base :: fun(int tmp):" << tmp << endl;

    }
};
class Derived : public Base{
public:
    virtual void fun(int tmp){
        cout << "Derived ::fun(int tmp)" << tmp << endl;

    }
};
int main(){
    //Base * p = new Derived();
    Derived ex;
    ex.fun(3); // Derived :: fun(int) :3
    ex.Base::fun(4); //Base::fun(int) :4 
    return 0;
}

重载和重写的区别:

  • 范围区别:对于类中函数的重载或者重写而言,重载发生在同一个类的内部重写发生在不同的类之间(子类和父类之间)。
  • 参数区别:重载的函数需要与原函数有相同的函数名不同的参数列表,不关注函数的返回值类型,重写的函数的函数名,参数列表和返回值类型都需要和原函数相同。父类中被重写的函数需要有virtual修饰
  • Virtual关键字:重写的函数基类中必须有virtual关键字修饰,重载的函数可以有virtual关键字的修饰也可以没有。

隐藏和重写,重载的区别:

范围区别:隐藏与重载范围不同。隐藏发生在不同类中。

参数区别:隐藏函数和被隐藏函数参数列表可以相同,也可以不同,但函数名一定相同。当参数不同时,无论基类中的函数是否被virtual修饰,基类函数都是被隐藏而不是重写。

如何理解C++是面向对象编程

说明:该问题最好结合自己的项目经历进行展开解释。或举一些恰当的例子,同时对比下面向过程编程。

  • 面向过程编程:一种以执行程序操作的过程或函数为中心编写软件的方法。程序的数据通常存储在变量中,与这些过程是分开的。所以必须将变量传递给需要使用它们的函数。缺点:随着程序变得越来越复杂,程序数据与运行代码的分离可能会导致问题。例如,程序的规范经常会发生变化,从而需要更改数据的格式或数据结构的设计。当数据结构发生变化时,对数据进行操作的代码也必须更改为接受新的格式。查找需要更改的所有代码会为程序员带来额外的工作,并增加了使代码出现错误的机会。
  • 面向对象编程(Objective-Oriented Programming ,OOP),以创建和使用对象为中心。一个对象(Object)就是一个软件实体,它将数据和程序在一个单元中组合起来。对象的数据项,也称其为属性,存储在成员变量中。对象执行的过程称其为成员函数。将对象的数据绑在一起则被称之为封装。

面向对象编程的进一步说明

面向对象编程将数据成员和成员函数封装到一个类中,并声明数据成员和成员函数的访问级别(public ,private,protected),以便控制类对象对数据成员和函数的访问,对数据成员起到一定的保护作用。而且在类的对象调用成员函数的时候,只知道成员函数的名,参数列表以及返回值类型即可,无需了解其函数的实现原理。当类内部的数据成员或者成员函数发生改变时不影响类外部的代码

多态的实现

多态:多态就是不同继承类的对象。对同一消息做出不同的响应,基类的指针呈现不同的表现方式。在基类函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数,如果对象类型是基类,就调用基类的函数。

实现方法:多态是通过虚函数实现的,虚函数的地址保存在虚函数表中,虚函数表的地址保存在含有虚函数的类的实例对象的内存空间中。

实现过程:

  • 1.在类中用virtual关键字声明的函数叫做虚函数
  • 2.存在虚函数的类都有一个虚函数表,当创建一个该类的对象时,该对象有一个指向虚函数表的虚表指针(虚函数表和类对应的,虚函数表指针和对象对应)。
  • 3.当基类指针指向派生类对象,基类指针调用虚函数时,基类指针指向派生类的虚表指针,由于该虚表指针指向派生类虚函数表,通过遍历虚表,寻找相应的虚函数。

简单解释:基类指针指向派生类对象时,通过派生类对象的虚表指针找到虚函数表,进而找到相应的虚函数。 Derive::f()进行调用。


评论
  目录