设计模式详解九:迭代器模式

迭代器模式, 即iterator模式,封装集合对象,提供访问集合的接口。

一、设计模式的结构图

迭代器模式的处理逻辑是:iterator迭代器内部保存持有集合对象,用于访问集合,而集合提供创建返回迭代器的函数接口。

二、程序代码实现

1、 定义实现迭代器的基类

#ifndef Object
typedef int Object;
#endif


/// 类定义
class Iterator
{
public:
    virtual ~Iterator();
    virtual void First() = 0;
    virtual void Next() = 0;
    virtual bool IsDone() = 0;
    virtual Object CurrentItem() = 0;
    
protected:
    Iterator();
private:
};


/// 类实现

Iterator::Iterator()
{
    
}

Iterator::~Iterator()
{
    
}

2、 定义实现迭代器的子类

/// 类的定义
class Aggregate;
class ConcreteIterator : public Iterator
{
public:
    ConcreteIterator();
    ConcreteIterator(Aggregate *ag, int idx = 0);
    ~ConcreteIterator();
    void First();
    void Next();
    bool IsDone();
    Object CurrentItem();

protected:
private:
    Aggregate* m_ag;
    int m_idx;
};

/// 类的实现
ConcreteIterator::ConcreteIterator()
{
    
}

ConcreteIterator::ConcreteIterator(Aggregate *ag, int idx)
{
    this->m_ag = ag;
    this->m_idx = idx;
}

ConcreteIterator::~ConcreteIterator()
{
}

Object ConcreteIterator::CurrentItem()
{
    return m_ag->GetItem(m_idx);
}

void ConcreteIterator::First()
{
    m_idx = 0;
}

void ConcreteIterator::Next()
{
    if (m_idx < m_ag->GetSize())
    {
        m_idx++;
    }
}

bool ConcreteIterator::IsDone()
{
    return (m_idx == m_ag->GetSize());
}

3、定义实现集合的基类

#ifndef Object
typedef int Object;
#endif

/// 类的定义
class Iterator;
class Aggregate
{
public:
    virtual ~Aggregate();
    virtual Iterator* CreateIterator() = 0;
    virtual Object GetItem(int idx) = 0;
    virtual int GetSize() = 0;
    
protected:
    Aggregate();
private:
};


/// 类的实现
Aggregate::Aggregate()
{
}

Aggregate::~Aggregate()
{
}

4、定义实现集合的子类

/// 类的定义
class ConcreteAggregate:public Aggregate
{
public:
    enum {SIZE = 3};
    ConcreteAggregate();
    ~ConcreteAggregate();
    Iterator* CreateIterator();
    Object GetItem(int idx);
    int GetSize();
    
protected: private:
    Object m_objs[SIZE];
};


/// 类的实现
ConcreteAggregate::ConcreteAggregate()
{
    for (int i = 0; i < SIZE; i++)
        m_objs[i] = i;
}

ConcreteAggregate::~ConcreteAggregate()
{
}

Iterator* ConcreteAggregate::CreateIterator()
{
    return new ConcreteIterator(this);
}

Object ConcreteAggregate::GetItem(int idx)
{
    if (idx < this->GetSize())
    {
        return m_objs[idx];
    }
    else
    {
        return -1;
    }
}

int ConcreteAggregate::GetSize()
{
    return SIZE;
}

 

三、测试验证结果

1、主程序输入如下所示的测试代码

Aggregate* ag = new ConcreteAggregate();
Iterator* it = new ConcreteIterator(ag);
for (; !(it->IsDone()) ; it->Next())
{
    std::cout<< "item = "<< it->CurrentItem()<<std::endl;
}

2、运行结果如下所示,迭代器访问集合内的所有数据,并输出显示

item = 0
item = 1
item = 2

四、总结

迭代器的实现过程中,为了避免集合过多的暴露公有接口,可以将迭代器声明为集合的友元,来访问集合的私有数据。另外,从上面的代码实现中,可以看出集合提供创建迭代器的接口,而迭代器持有集合对象,内部包装集合的访问接口。

C++11:启动线程的五种方式

程序开发过程中,面对各种各样的需求场景,其中涉及到线程的应用,本文将总结启动线程的五种方式,方便后续复习以及查阅。

  1. 函数指针方式启动线程
  2. 函数对象方式启动线程
  3. Lambda函数的方式启动线程
  4. 类函数指针的方式启动线程
  5. std::bind的方式启动线程

一、函数指针方式

1、 定义实现线程处理函数,然后启动线程调用该处理函数

void ThreadFunction(void)
{
    std::cout << __func__ << ": thread executing "  << std::endl;
}

// 函数指针方式启动线程
void JTestThread::TestStartThread_Method01(void)
{
    std::thread thread_obj(ThreadFunction);
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、 运行结果

ThreadFunction: thread executing

二、函数对象方式

1、创建类ThreadOpeartor,然后重载operator()运算符,然后以仿函数的形式作为线程的参数来启动线程

class ThreadOpeartor
{
public:
    void operator()()
    {
        std::cout << __func__ << ":  thread executing "  << std::endl;
    }
    
};

// 函数对象方式启动线程
void JTestThread::TestStartThread_Method02(void)
{
    std::thread thread_obj( (ThreadOpeartor()) ) ;
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、运行结果

operator():  thread executing

三、lambda方式

1、lambda函数即匿名函数作为线程的参数来启动线程

// Lambda函数的方式启动线程
void JTestThread::TestStartThread_Method03(void)
{
    std::thread thread_obj( []{
        std::cout << __func__ << ": lambda thread executing" << std::endl;
    } ) ;
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、运行结果

operator(): lambda thread executing

四、类函数指针方式

1、定义类成员函数,然后以函数指针作为参数来启动线程

void JTestThread::TestThread04(void)
{
    std::cout << __func__ << ": thread executing" << std::endl;
}

// 类函数指针的方式启动线程
void JTestThread::TestStartThread_Method04(void)
{
    std::thread thread_obj(&JTestThread::TestThread04, this) ;
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、运行结果

TestThread04: thread executing

五、std::bind方式

1、类成员函数作为stb::bind的参数, 然后stb::bind作为线程参数来启动线程

void JTestThread::TestThread05(void)
{
    std::cout << __func__ << ": thread executing" << std::endl;
}

// std::bind的方式启动线程
void JTestThread::TestStartThread_Method05(void)
{
    std::thread thread_obj(std::bind(&JTestThread::TestThread05, this)) ;
    if (thread_obj.joinable())
    {
        thread_obj.join();
    }
}

2、运行结果

TestThread05: thread executing

六、总结

  • 函数指针方式:定义普通函数
  • 函数对象方式:重载operator()运算符
  • Lambda函数方式:匿名函数
  • 类函数指针的方式:对象函数指针
  • std::bind的方式: 实际上也是对象函数指针

七、参考链接

如何正确创建线程

怎样通过函数对象创建独立线程

设计模式详解八:Starategy模式

strategy策略模式就是实现和抽象解耦,逻辑处理放到Context中,并在逻辑处理中调用抽象接口,而组合对象实现具体的内容。

一、设计模式的结构图

策略模式context实现主要逻辑,具体实现放到strategy类,strategy是抽象基类,子类继承该基类,实现具体的功能,context与strategy是一对多的组合关系,这样实现的好处就是context可以支持多个strategy类型对象,strategy类型也可以复用到其他不同的逻辑算法。

二、程序代码实现

1、定义实现抽象基类Strategy

class Strategy
{
public:
    Strategy();
    virtual ~Strategy();
    virtual void AlgrithmInterface() = 0;

protected:
private:
};

Strategy::Strategy()
{
}

Strategy::~Strategy()
{
    DEBUG_LOG("", "~Strategy...");
}

void Strategy::AlgrithmInterface()
{
}

2、定义实现基类Strategy的子类ConcreteStrategyA

class ConcreteStrategyA:public Strategy
{
public:
    ConcreteStrategyA();
    virtual ~ConcreteStrategyA();
    void AlgrithmInterface();

protected:
private:
};

ConcreteStrategyA::ConcreteStrategyA()
{
}

ConcreteStrategyA::~ConcreteStrategyA()
{
    DEBUG_LOG("", "~ConcreteStrategyA...");
}

void ConcreteStrategyA::AlgrithmInterface()
{
    DEBUG_LOG("", "ConcreteStrategyA::AlgrithmInterface...");
}

3、定义实现基类Strategy的子类ConcreteStrategyB

class ConcreteStrategyB:public Strategy {
public:
    ConcreteStrategyB();
    virtual ~ConcreteStrategyB();
    void AlgrithmInterface();
    
protected:
private:
};

ConcreteStrategyB::ConcreteStrategyB()
{
}

ConcreteStrategyB::~ConcreteStrategyB()
{
    DEBUG_LOG("", "~ConcreteStrategyB...");
}

void ConcreteStrategyB::AlgrithmInterface()
{
    DEBUG_LOG("", "ConcreteStrategyB::AlgrithmInterface...");
}

4、定义实现Context类

class Context
{
public:
    Context(Strategy* stg);
    ~Context();
    void DoAction();
protected:
private:
    Strategy* m_stg;
};

Context::Context(Strategy* stg)
{
    m_stg = stg;
}

Context::~Context()
{
    if (!m_stg)
        delete m_stg;
}
void Context::DoAction()
{
    m_stg->AlgrithmInterface();
}

三、测试验证结果

1、主程序输入如下所示的测试代码

Strategy* p_strategy;
p_strategy = new ConcreteStrategyA();
Context* p_context = new Context(p_strategy);
p_context->DoAction();
if (NULL != p_context)
{
    delete p_context;
    p_context = NULL;
}

2、运行结果如下图所示,Context对象内部最终调用ConcreteStrategyA对象的算法功能。

[2019-06-30 14:30:24.680  AlgrithmInterface:35]   = ConcreteStrategyA::AlgrithmInterface...

四、总结

策略模式,不变的逻辑算法放到Context对象中实现,而动态变化的算法实现则由Strategy子类来实现,这里有一点需要注意的是Context析构的时候,也会析构Strateby类型的对象,所以,外部定义的Strateby对象不需要再次进行delete。

设计模式详解七:Adapter模式

Adapter模式就是当已经设计好的接口与第三方库的接口不一致的时候,可以起到兼容的作用。Adapter模式一般分为:类模式(继承方式)和对象模式(组合方式)。

一、设计模式的结构图

1、类模式是采用继承的方式来复用Adaptee的接口

2、对象模式是采用组合的方式来调用Adaptee接口

二、程序代码实现

1、定义和实现用户的接口Target

//定义用户接口
class Target
{
public:
    Target();
    virtual ~Target();
    virtual void Request();
    
protected:
private:
};

//实现用户接口
Target::Target()
{
}

Target::~Target()
{
}

void Target::Request()
{
    DEBUG_LOG("", "Target::Request...");
}

2、 定义和实现第三方库接口Adaptee

//第三方
class Adaptee
{
public:
    Adaptee();
    ~Adaptee();
    void SpecificRequest();

protected:
private:
};


Adaptee::Adaptee()
{
}

Adaptee::~Adaptee()
{
}

void Adaptee::SpecificRequest()
{
    DEBUG_LOG("", "Adaptee::SpecificRequest...");
}

3、定义和实现类模式的适配器

//适配器1
class AdapterOne:public Target,private Adaptee
{
public:
    AdapterOne();
    ~AdapterOne();
    void Request();
    
protected:
private:
};


AdapterOne::AdapterOne()
{
}

AdapterOne::~AdapterOne()
{
}

void AdapterOne::Request()
{
    this->SpecificRequest();
}

4、定义和实现对象模式的适配器

//适配器2
class AdapterTwo:public Target
{
public:
    AdapterTwo(Adaptee* ade);
    ~AdapterTwo();
    void Request();

protected:
private:
    Adaptee* _ade;
};


AdapterTwo::AdapterTwo(Adaptee* ade)
{
    this->_ade = ade;
}

AdapterTwo::~AdapterTwo()
{
}

void AdapterTwo::Request()
{
    _ade->SpecificRequest();
}

三、测试验证结果

1、主函数输入如下所示的测试代码段,用来测试对象模式的适配器模式

Adaptee* ade = new Adaptee;
Target* adt = new AdapterTwo(ade);
adt->Request();

2、运行结果如下图所示,调用Request之后,内部实际上调用的是第三方库接口

[2019-06-23 13:51:20.713  SpecificRequest:36]   = Adaptee::SpecificRequest...

四、总结

适配器顾名思义就是一个转接口,将目标对象和第三方连接起来,Target与Adapter之间存在多态的特性,而Adapter内部调用Adaptee完成实际的功能。

设计模式详解六:装饰者模式

Decorator模式,即装饰者模式是通过组合的方式给类添加新的职责。通过组合的好处是不会增加类的继承深度。

一、设计模式的结构图

结构图中,SubComponent和Decorator都继承自同一个基类Component, SubComponet继承自Component基类,这个体现多态的特性,大家应该都明白。而Decorator也继承自Component基类是为什么呢?我的理解是,Decorator是为component添加新的职责,它应该是属于Component,后续才能同SubComponent一样的方式进行使用。Decorator作为SubDecorator的基类,是为了能够提供更多的修饰,比如后续有新的修饰SubDecorator2,那么原来的实现的接口是不需要修改的。

二、程序代码实现

1、定义和实现Component基类

class Component
{
public:
    virtual ~Component();
    virtual void Operation();
protected:
    Component();
private:
};


Component::Component()
{
}

Component::~Component()
{
}

void Component::Operation()
{
}

2、定义和实现SubComponent

class SubComponent:public Component
{
public:
    SubComponent();
    ~SubComponent();
    void Operation();

protected:
private:
};

SubComponent::SubComponent() {}

SubComponent::~SubComponent() {}

void SubComponent::Operation()
{
    DEBUG_LOG("", "SubComponent operation..." );
}

3、定义和实现Decorator类

class Decorator:public Component
{
public:
    Decorator(Component* com);
    virtual ~Decorator();
    void Operation();
    
protected:
    Component* m_com;
private:
};


Decorator::Decorator(Component* com)
{
    this->m_com = com;
}

Decorator::~Decorator()
{
    delete m_com;
}

void Decorator::Operation()  {}

4、定义和实现SubDecorator

class SubDecorator:public Decorator
{
public:
    SubDecorator(Component* com);
    ~SubDecorator();
    void Operation();
    void AddedBehavior();
    
protected:
private:
};


SubDecorator::SubDecorator(Component* com):Decorator(com)  {}

SubDecorator::~SubDecorator()  {}

void SubDecorator::AddedBehavior()
{
    DEBUG_LOG("", "SubDecorator::AddedBehacior...." );
}

void SubDecorator::Operation()
{
    m_com->Operation();
    this->AddedBehavior();
}

三、测试验证结果

程序输入如下所示的代码段,然后运行调试查看结果:

Component* com = new SubComponent();
Decorator* dec = new SubDecorator(com);
dec->Operation();
//com在dec在析构函数中释放
delete dec;

运行结果如下所示,SubDecorator内部调用SubComponent后,并且为SubComponent添加新的行为。

[2019-06-02 16:20:02.135  Operation:29]   = SubComponent operation...
[2019-06-02 16:20:02.135  AddedBehavior:53]   = SubDecorator::AddedBehacior....

四、总结

Decorator模式提供了一种添加职责的方式,采用组合的方式,因此,当需要添加操作的时候,可以考虑Decorator模式来解决。

备注:Decorator与Component之间是整体与部分的关系,一个Decorator可以对应多个Component对象。

设计模式详解五:Factory模式


创建Factory模式(工厂模式),主要有两个目的,(1)如果一个基类有很多的子类,那么可以通过Factory模式统一创建子类对象, 这样可以达到方便维护,提高扩展性的目的; (2) 将具体化类的工作延迟到子类中,举个例子:父类A会调用到类B,进行逻辑的判断处理,但是类B是一个抽象类,类A不知道类B的具体类是什么,当创建类A的子类C就可以知道类B的子类D,这样就可以达到将不变的逻辑放在父类处理,而将具体的实现放到子类中。

一、设计模式的结构图

1、Factory类提供创建类Product的接口CreateProduct,这是工厂模式的第一个作用:负责维护众多类的创建。

2、Factory类是抽象类,Product也是抽象类,可以在抽象类中做公共的处理,而具体的实现工作延迟到子类,这就是工厂模式的第二个作用。

二、程序代码实现

首先定义和实现生产基类和子类

1、定义生产基类和它的子类

class Product;

//生产基类
class Product
{
public:
    virtual ~Product() = 0;
    
protected:
    Product();
    
private:
};

//生产子类
class SubProduct:public Product
{
public:
    ~SubProduct();
    SubProduct();
    
protected:
private:
};

2、实现生产基类和它的子类

Product::Product()
{
}

Product::~Product()
{
}

SubProduct::SubProduct()
{
    DEBUG_LOG("", "SubProduct...");
}

SubProduct::~SubProduct()
{
}

 

下面是定义和实现工厂的基类和它的子类

1、定义工厂的基类和它的子类

//工厂基类
class Factory
{
public:
    virtual ~Factory() = 0;
    virtual Product* CreateProduct() = 0;
    
protected:
    Factory();
    
private:
};

//工厂子类
class SubFactory:public Factory
{
public:
    ~SubFactory();
    SubFactory();
    
    Product* CreateProduct();
protected:
    
private:
};

2、实现工厂的基类和它的子类

Factory::Factory()
{
}

Factory::~Factory()
{
}

SubFactory::SubFactory()
{
    DEBUG_LOG("", "SubFactory...");
}

SubFactory::~SubFactory()
{
}

Product* SubFactory::CreateProduct()
{
    return new SubProduct();
}

三、测试验证结果

程序输入如下所示的代码段,然后运行调试查看结果:

Factory *fac = new SubFactory();
Product *product = fac->CreateProduct();

delete fac;
delete product;

运行结果如下所示,构造具体的工厂对象,然后工厂对象创建具体的生产对象。

[2019-05-25 14:03:49.814  SubFactory:45]   = SubFactory...
[2019-05-25 14:03:49.814  SubProduct:27]   = SubProduct...

四、总结

工厂模式可以解决两个问题,第一个是负责对象的创建,第二个是将具体类的工作延迟到子类中。上面的示例中,通过SubFactory调用CreateProduct创建具体类SubProduct,如果这时候又要创建一个新的类SubProduct2,  那么有得在SubFactory中创建方法CreateProduct2来创建具体类SubProduct2,这样类SubFactory就封闭不了,那么解决的办法就是创建Factory的一个新的子类SubFactory2。另一个方式就是CreateProduct方法提供一个参数,然后通过参数来决定创建具体的Product。

设计模式详解四:Bridge模式

Bridge模式是将两个结合的对象进行解耦,以便达到一个对象的修改不会影响到另一个对象的目的。比如,不同操作系统和不同算法的两个对象,操作系统可以调用不同的算法,而不同的算法可以运行在不同的操作系统上。这里操作系统我们认为是抽象部分,算法是实现部分。

一、设计模式的结构图

Abstraction对应操作系统部分, Imp对应算法部分。Abstraction是操作系统的基类,SubAbstraction是该基类的子类,负责实现操作系统上的公共的部分。Imp是算法的基类,SubImpA和SubImpB是该基类的子类,负责实现算法功能。Abstraction和Imp之间是一对多的关系,一个操作系统可以支持多种算法。具体形象的结构图如下图:

二、程序代码实现

首先定义实现操作系统的部分。

1、定义操作系统的基类以及它的子类

//操作系统基类
class Abstraction
{
public:
    virtual ~Abstraction();
    virtual void Operation() = 0;

protected:
    Abstraction();
    private:
};

//具体操作系统的实现
class SubAbstraction:public Abstraction {
public:
    SubAbstraction(Imp* imp);
    ~SubAbstraction();
    void Operation();

protected:
    
private:
    Imp* m_imp;
};

2、实现操作系统的基类以及它的子类

Abstraction::Abstraction()
{
}
Abstraction::~Abstraction()
{
}


SubAbstraction::SubAbstraction(Imp* imp)
{
    m_imp = imp;
}

SubAbstraction::~SubAbstraction()
{
}

void SubAbstraction::Operation()
{
    m_imp->Operation();
}

然后定义实现算法的部分

1、定义算法的基类以及它的子类

//算法的基类
class Imp
{
public:
    virtual ~Imp();
    virtual void Operation() = 0;

protected:
    Imp();
private:
};

//具体算法的实现A
class SubImpA:public Imp
{
public:
    SubImpA(); ~SubImpA();
    virtual void Operation();
    
protected:
private:
};

//具体算法的实现B
class SubImpB:public Imp
{
public:
    SubImpB(); ~SubImpB();
    virtual void Operation();
    
protected:
private:
};

2、实现算法的基类以及它的子类

Imp::Imp()
{
}

Imp::~Imp()
{
}

void Imp::Operation()
{
    DEBUG_LOG("", "call Imp");
}


SubImpA::SubImpA()
{
}

SubImpA::~SubImpA()
{
}

void SubImpA::Operation()
{
    DEBUG_LOG("", "call SubImpA");
}


SubImpB::SubImpB()
{
}

SubImpB::~SubImpB()
{
}

void SubImpB::Operation()
{
    DEBUG_LOG("", "call SubImpB");
}

三、测试验证结果

主程序中输入如下的代码段,然后编译运行查看它的运行结果

Imp *imp = new SubImpA();
Abstraction *abs = new SubAbstraction(imp);
abs->Operation();

Imp *imp2 = new SubImpB();
Abstraction *abs2 = new SubAbstraction(imp2);
abs2->Operation();

运行结果如下所示,操作系统和算法的对象通过组合的方式结合,操作系统根据不同的算法对象调用不同的算法,而操作系统内部的处理逻辑是不需要改变的。

[2019-05-19 21:16:12.453  Operation:61]   = call SubImpA
[2019-05-19 21:16:12.453  Operation:73]   = call SubImpB

四、总结

Bridge模式的原理是采用组合的方式,将两个对象结合起来,两个对象各自实现内部的处理逻辑,但是对外接口保持不变。比如上面的操作系统和算法两个对象,操作系统结合C++的多态特性,调用算法的接口,而算法接口不变,根据不同的算法内部实现不同的算法处理逻辑。当增加新的操作系统的时候,算法不需要修改。当增加新的算法的时候,操作系统也不需要修改。真正实现两个对象解耦的目的。

 

设计模式详解三:Observer模式(观察者模式)

观察者模式是一种“一(Subject)对多(Observer)”的设计模式,并且当“一”发生变化的时候,对应的“多”也能够同步收到通知或者改变。

一、设计模式的结构图

Observer模式的结构图:Subject目标基类中的Attatch函数实现将Observer指针存储到list列表,Detach函数实现将Observer指针从list列表中移除,Notify函数内部是循环检查list列表中存放的Observer指针,如果存在,那么调用Observer实现的Update函数。Observer观察者基类定义Update虚函数,它的继承者SubObserverA、SubObserverB……实现Update函数。

二、程序代码实现

1、首先定义目标基类Subject以及它的子类SubSubject

class Observer;

////目标基类
class Subject
{
public:
    virtual ~Subject();
    virtual void Attach(Observer* obv);
    virtual void Detach(Observer* obv);
    virtual void Notify();
    
protected:
    
    Subject();
private:
    list<Observer* >* m_obvs;
};

////目标实现类
class SubSubject:public Subject
{
public:
    SubSubject();
    ~SubSubject();

protected:

private:
    
};

2、实现目标基类以及它的子类SubSubject

////目标基类
Subject::Subject()
{
    m_obvs = new list<Observer*>;
}

Subject::~Subject()
{
    
}

void Subject::Attach(Observer* obv)
{
    m_obvs->push_front(obv);
}

void Subject::Detach(Observer* obv)
{
    if (obv != NULL) m_obvs->remove(obv);
}

void Subject::Notify()
{
    list<Observer*>::iterator it;
    it = m_obvs->begin();
    for (;it != m_obvs->end();it++)
    {
        (*it)->Update(this);
    }
}

////目标子类
SubSubject::SubSubject()
{
}

SubSubject::~SubSubject()
{
}

3、定义观察者基类,声明Update为虚函数,同时定义观察者的子类A和观察者的子类B,并且子类A和子类B维护者指向Subject的指针。

//// Observer观察者的基类
class Observer
{
public:
    virtual ~Observer();
    virtual void Update(Subject* sub) = 0;
    
protected:
    Observer();
private:
};

//// Observer观察者的子类A
class SubObserverA:public Observer
{
public:
    virtual Subject* GetSubject();
    SubObserverA(Subject* sub);
    virtual ~SubObserverA();
    
    //传入 Subject 作为参数,这样可以让一个 View 属于多个的 Subject。
    void Update(Subject* sub);
private:
    Subject* _sub;
};

//// Observer观察者的子类B
class SubObserverB:public Observer
{
public:
    virtual Subject* GetSubject();
    SubObserverB(Subject* sub);
    virtual ~SubObserverB();
    
    //传入 Subject 作为参数,这样可以让一个 View 属于多个的 Subject。
    void Update(Subject* sub);
    
private:
    Subject* _sub;
};

4、实现观察者基类,观察者的子类A和观察者的子类B

//// Observer观察者的基类
Observer::Observer()
{
}

Observer::~Observer()
{
}

//// Observer观察者的子类A
SubObserverA::SubObserverA(Subject* sub)
{
    _sub = sub;
    _sub->Attach(this);
}

SubObserverA::~SubObserverA()
{
    _sub->Detach(this);
    if (_sub != 0)
        delete _sub;
}

Subject* SubObserverA::GetSubject()
{
    return _sub;
}

void SubObserverA::Update(Subject* sub)
{
    DEBUG_LOG("", "SubObserverA update");
    // 由于SubObserverA持有Subject的指针,因此这里可以去调用Subject的相关方法
}

//// Observer观察者的子类B
SubObserverB::SubObserverB(Subject* sub)
{
    _sub = sub;
    _sub->Attach(this);
}

SubObserverB::~SubObserverB()
{
    _sub->Detach(this);
    if (_sub != 0)
        delete _sub;
}

Subject* SubObserverB::GetSubject()
{
    return _sub;
}

void SubObserverB::Update(Subject* sub)
{
    DEBUG_LOG("", "SubObserverB update");
    // 由于SubObserverB持有Subject的指针,因此这里可以去调用Subject的相关方法
}

三、测试验证结果

代码实现完成之后,主程序输入如下所示的代码段,用于验证subject调用Notify的时候是否会通知到注册到它的列表中的观察者对象。

SubSubject * sub = new SubSubject();
Observer *obsA = new SubObserverA(sub);
Observer *obsB = new SubObserverB(sub);

sub->Notify();

运行结果如下所示,从结果看Subject调用Notify函数之后,就会调用注册到列表中的观察者对象的Update函数。

[2019-05-12 22:40:09.426  Update:110]   = SubObserverB update
[2019-05-12 22:40:09.426  Update:85]   = SubObserverA update

由于观察者对象维护者Subject的指针,因此,可以通过观察者对象来间接调用Notify函数

SubSubject * sub = new SubSubject();
Observer *obsA = new SubObserverA(sub);
Observer *obsB = new SubObserverB(sub);

SubObserverA *obsA2 =dynamic_cast<SubObserverA *>(obsA);
obsA2->GetSubject()->Notify();

四、MVC结构的理解

MVC架构是Observer的一个实例,Observer就是观察者, Observable对应目标Subject,M代表Model,V代表View,C代表Control, Control为控制模块,接收到用户的输入信息之后,传递给Model模块,该模块进行业务处理,最后展示出不同的视图View。

五、总结

观察者模式也称为发布订阅模式,目标就是就是通知的发布者,观察者是通知的订阅者。目标可以通知观察者,并且由于观察者持有目标对象的指针,因此,观察者也可以主动触发目标去通知其他的观察者对象。

 

理解策略路由

一、linux内置的路由表

linux默认三种路由表,存放在/etc/iproute2/rt_tables

local:  本地接口地址,广播地址,以及NAT地址都放在这个表。该路由表由系统自动维护,管理员不能直接修改。
main:  执行 route -n就是读取这张表的信息。如果没有指明路由所属的表,所有的路由都默认放在这个表里。
default: 默认的路由。

[root@f8s home]# ip rule show
0:      from all lookup local 
32766:  from all lookup main 
32767:  from all lookup default

二、规则和路由的关系

规则(ip rule)控制使用那个路由表,ip table往路由表中设置路由信息

三、设置策略路由

结合例子说明设置策略路由的过程

1、创建一个虚拟网卡eth2.300

# 创建虚拟网卡
vconfig add eth2 300
# 设置虚拟网卡的IP地址,并启用网卡
ifconfig eth2.300 192.168.100.50 netmask 255.255.255.0 up

通过命令ifconfig查看网卡信息

[root@f8s home]# ifconfig 
eth2      Link encap:Ethernet  HWaddr 00:0C:29:87:80:CD  
          inet addr:192.168.255.128  Bcast:192.168.255.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe87:80cd/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:90005 errors:0 dropped:0 overruns:0 frame:0
          TX packets:87454 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:15158203 (14.4 MiB)  TX bytes:13314917 (12.6 MiB)
          Interrupt:18 Base address:0x2000 
 
eth2.300  Link encap:Ethernet  HWaddr 00:0C:29:87:80:CD  
          inet addr:192.168.100.50  Bcast:192.168.100.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe87:80cd/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:27 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 

2、创建一条规则, 从地址192.168.100.50发送的数据,都走路由表100

# 创建路由规则
ip rule add from 192.168.100.50/32 table 100

查看规则信息:

[root@f8s home]# ip rule show
0:      from all lookup local 
32765:  from 192.168.100.50 lookup 100 
32766:  from all lookup main 
32767:  from all lookup default 

3、往路由表100,添加路由信息

# 发到192.168.100.0/24网段的网络包,从网卡eth2.300发生出去,包的源IP地址设为192.168.100.50
ip route add 192.168.100.0/24 dev eth2.300 src 192.168.100.50 table 100

路由表table 100的路由信息:

[root@f8s home]# ip route show table 100
192.168.100.0/24 dev eth2.300  scope link  src 192.168.100.50

四、总结

策略路由:首先通过ip rule定义规则,确认使用那个路由表,然后通过ip route往路由表中添加路由信息。

 

 

 

 

 

自制智能指针

一、智能指针的原理

智能指针就是封装了创建对象的指针,并且可以在对象过期的时候,让析构函数自动删除指向的内存。

二、代码实现

为了防止拷贝和赋值,我们将拷贝函数和赋值构造函数放到private私有区域内。智能指针的类模版的实现方式:

template <typename T>
class SmartPtr
{
public:
    // explicit防止隐式转换
    explicit SmartPtr(T *pObj);
    ~SmartPtr(void);

public:
    T* operator->();
    T* Get();
    void Reset(T* pObj);
    
private:
    // 防止拷贝和赋值
    SmartPtr(const SmartPtr &);
    SmartPtr & operator= (const SmartPtr &);
    
    T* m_pObj;
};

template<typename T>
SmartPtr<T>::SmartPtr(T *pObj):m_pObj(pObj)
{
    
}

template<typename T>
SmartPtr<T>::~SmartPtr(void)
{
    delete m_pObj;
    m_pObj = 0;
}

template<typename T>
T* SmartPtr<T>::operator->()
{
    return m_pObj;
}

template<typename T>
T* SmartPtr<T>::Get()
{
    return m_pObj;
}

template<typename T>
void SmartPtr<T>::Reset(T* pObj)
{
    delete m_pObj;
    m_pObj = m_pObj;
}

三、测试验证

为了验证智能指针在退出的时候是否会自动调用delete来释放动态创建的对象,我们定义实现ObjectA对象。具体的实现细节如下所示:

class ObjectA
{
public:
    // 构造函数
    ObjectA(int iNum);
    ObjectA();
    // 析构函数
    ~ObjectA();
 
private:
    int m_num;
};
ObjectA::ObjectA(int iNum):m_num(iNum)
{
    cout << "ObjectA constructor" << endl;
}

ObjectA::ObjectA()
{
    cout << "ObjectA constructor, no param" << endl;
}

ObjectA::~ObjectA()
{
    cout << "ObjectA destructor" << endl;
}

完成上面ObjectA对象的定义和实现后,接下来就开始进行验证,验证代码如下:

ObjectA *pobj = new ObjectA;
SmartPtr<ObjectA> smartPtr(pobj);

通过调试运行,最后输出的结果为:

ObjectA constructor, no param
ObjectA destructor

这说明智能指针调用自身的析构函数,而析构函数中又回去删除动态创建对象的内存。

四、注意事项

因为模版不是函数,不能单独编译,所以实现智能指针的模版类的时候,需要将所有模版信息放入头文件中,其他文件使用的时候,再包含该文件。

五、总结

智能指针的机制就是利用本地变量在退出函数之后会自动删除的特性。首先保存动态创建对象的指针,当本地变量的生命周期结束的时候,自动调用其析构函数,而析构函数则会释放保存的指针指向的内存,从而达到避免调用new而忘记delete导致出现内存泄露的问题。