Effectiv C++条款31 将文件间的编译依存关系降至最低 Handle Class和Interface Class完整实现

本文介绍C++中的Handleclass和Interfaceclass设计模式。Handleclass通过分离接口和实现降低编译依赖,提高模块独立性。Interfaceclass通过抽象基类提供稳定的接口,使实现变化不影响客户端代码。
//条款31 将文件间的编译依存关系降至最低   
/* 
Handle class 
简而言之,将类的接口和实现分离。 
接口里面存储一个指针,指向实现类,然后客户调用接口。 
这样当实现改变时候,客户不用重编译。 
 
Handle Class (Person.h PersonImpl.h PersonImpl.cpp) 
<strong>Person的客户就与Person的实现细目分离,Person实现的修改不需要Person客户端重新编译。</strong> 
接口与实现分离。一个只负责提供接口,另一个负责实现该接口。main class(Person)内含有一个指针成员 
shared_ptr<PersonImpl>pImpl.(pimpl pointer to implementation)。Person客户完全与Person实现细目分离。 
这个分离的关键在于以“声明的依存性”替换“定义的依存性”。现实中让头文件尽可能自我满足,万一做不到, 
则让它与其它文件内的声明式(而非定义式)相依。 
*/  
  
/* 
Interface class 
简而言之,将类抽象为接口,然后客户调用接口,这样当类的实现改变时不会受影响。 
<strong>类的实现通过继承抽象基类完成,提供给客户的是抽象类的接口,所以实例基类改变,不会导致客户调用基类接口的文件都重新编译。 </strong>
<strong>客户调用的构造接口是static,因为它定义在抽象基类中,而抽象基类不能实例化, 
只能在实例化派生类时部分实例化,所以定义成static。static的函数属于整个类,</strong> 
可以通过类名::函数名的方式调用,具体构造函数在派生类中定义。 
 
 
另一种制作Handle class的办法是,令Person称为抽象基类,称为: 
Interface Class (Person2.h Person2.cpp RealPerson2.h) 
Person类定义成抽象基类,成为接口。这个class的客户必须以Person的指针或引用来撰写应用程序。 
Interface class的客户通常调用factory(工厂)函数或virtual构造函数。它们返回指针(或智能指针),指向 
动态分配所得对象,而该对象支持Interface class的接口。这样的函数又往往在Interface class内被 
声明为static。 
支持Interface class接口的那个具象类(concrete classes)必须被定义出来,而且真正的构造函数必须被调用。 
它提供继承而来的virtual函数的实现。 
 
*/  


Handle class

Person.h

#include <string>   
#include <memory>   
using namespace std;  
  
class PersonImpl;                       //  Person实现类的前置声明  
  
class Person  
{  
public:  
    Person(const string& name);  
    string getname() const;  
private:      
    tr1::shared_ptr<PersonImpl> pImpl;    // 指针,指向实现物,或者PersonImpl* pImpl  
    string name;  
};  

Person.cpp
#include "stdafx.h"   
#include "Person.h"   
#include "PersonImpl.h"   
  
Person::Person(const string& name) : pImpl(new PersonImpl(name))  
{  
    this->name = name;  
}  
  
string Person::getname() const  
{  
    return pImpl->getname();  
}  

PersonImpl.h

#include <string>   
#include <iostream>   
using namespace std;  
class PersonImpl  
{  
public:  
    PersonImpl(const string& name)  
    {  
        this->name = name;  
        //cout << "Impl" << endl;   
    }  
    string getname() const;  
private:  
    string name;  
};  

PersonImpl.cpp

#include "stdafx.h"   
#include "PersonImpl.h"   
string PersonImpl::getname() const  
{  
    return this->name;  
}  


main.cpp

#include "stdafx.h"  
#include "Person.h"  
#include <memory>  
#include <iostream>  
using namespace std;  
int _tmain(int argc, _TCHAR* argv[])  
{  
    // Handle Class  
    Person p("Yerasel");  
    cout << p.getname() << endl;  
  
  
    return 0;  
}  


以上方法是pImpl方法,它是微软的Herb Sutter提出来的,该方法是为了尽量减小接口和实现之间的耦合,以避免接口改动对程序重新编译等带来的影响。简单来说,如果你的大型程序因为复杂的头文件包含关系,使得你对某头文件的某小改动可能引起的巨大编译时间成本望而生畏,那么你需要用pImpl方法来改善这种处境。


抽象类(abstract class)

抽象类是指含有纯虚函数的类(至少有一个纯虚函数, 纯虚函数 virtual void f() = 0;  ),该类不能创建对象(抽象类不能实例化),但是可以声明指针和引用,用于基础类的接口声明和运行时的多态。

抽象类中,既可以有抽象方法,也可以有具体方法或者叫非抽象方法。抽象类中,既可以全是抽象方法,也可以全是非抽象方法。一个继承于抽象类的子类,只有实现了父类所有的抽象方法才能够是非抽象类。

接口

接口是一个概念。它在C++中用抽象类来实现,在C#和Java中用interface来实现。

接口是专门被继承的。接口存在的意义也是被继承。和C++里的抽象类里的纯虚函数是相同的。不能被实例化。

java中的接口只能含有方法,不能有成员变量,而c++的抽象类中可以含有成员变量。
定义接口的关键字是interface,例如:   
public interface MyInterface{   
public void add(int x,int y);   
public void volume(int x,int y,int z);   
}  

通过子类继承抽象类(接口),来进行实例化。


Interface class

Person2.h

#ifndef PER2_H   
#define PER2_H   
#include <string>   
#include <memory>   
using namespace std;  
  
class Person2  
{  
public:  
  
    Person2(const string& name)  
    {  
        this->thename = name;  
    }  
    virtual ~Person2(){};  
      
    static tr1::shared_ptr<Person2> create(const string& name);  
    virtual string getname() const = 0;  
private:  
    string thename;  
};  
#endif  


Person2.cpp
#include "stdafx.h"   
  
#include "Person2.h"   
#include "RealPerson2.h"   
#include <memory>   
  
  
tr1::shared_ptr<Person2> Person2::create(const string& name)  
{  
    return tr1::shared_ptr<Person2>(new RealPerson2(name));  
}  

RealPerson2.h

#ifndef REAL2_H   
#define REAL2_H   
#include "Person2.h"   
#include <iostream>   
using namespace std;  
  
class RealPerson2 : public Person2  
{  
public:  
    RealPerson2(const string& name)  
        :Person2(name), thename(name)  
    {  
    }  
    virtual ~RealPerson2(){}  
    string getname() const  
    {  
        return this->thename;  
    }  
private:  
    string thename;  
};  
  
#endif  


main.cpp
#include "Person.h"  
#include <memory>  
#include <iostream>  
using namespace std;  
int _tmain(int argc, _TCHAR* argv[])  
{  
// Interface Class  
    tr1::shared_ptr<Person2>p2 = (Person2::create("jandosim"));  
    cout << p2->getname() << endl;  
    return 0;  
}
附注,shared_ptr示例  
//工厂模式  
class abstract  
{  
public:  
    virtual void f()=0;  
    virtual void g()=0;  
protected:  
    virtual ~abstract(){}  
};  
  
class impl:public abstract  
{  
public:  
    virtual void f()  
    {  
        cout << "class impl f" << endl;  
    }  
    virtual void g()  
    {  
        cout << "class impl g" << endl;  
    }  
};  
  
tr1::shared_ptr<abstract> create()  
{  
    return tr1::shared_ptr<abstract>(new impl);  
}
int _tmain(int argc, _TCHAR* argv[])  
{  
    tr1::shared_ptr<abstract> a = create();  
    a->f();  
    a->g();  
    return 0;  
}

原文地址:http://blog.youkuaiyun.com/ozwarld/article/details/7101974

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值