条款38-40:通过复合塑模出has-a或"根据某物实现出"与private继承、多继承

本文深入探讨了C++模板中的继承与复合在类设计中的应用,包括选择公共继承还是复合,以及在何时使用私有继承和多重继承。通过具体案例分析,阐述了在设计类时如何明智地应用这些技术,以实现高效、灵活且易于维护的代码结构。
摘要由CSDN通过智能技术生成

条款38:通过复合塑模出has-a或"根据某物实现出"

复合是类型之间的一种关系,当某种类型的对象内包含其他类型的对象,我们说这是一种复合。

public继承是is-a关系,表示是一种的意思。复合意味着has-a关系,表示有一个或者is-implemented-in-terms-of(根据某物实现出)。

现在假设你需要一个template制造出一组classes表示不重复的对象组成的sets。你第一个想到的就是set容器。但是set容器考虑的是平衡,现在你考虑的是空间比效率更主要。所以你的底层选作List,那么是继承好呢,还是复合好呢?

先看继承:

 template<typename T>

class Set:public std::List<T>{........};

我们已经很清楚的知道了public继承表示的is-a关系。也就是Set是一个List。但是List是可以插入相同元素的。这与public继承不相符合。

所以好的方法是复合

template<class T>

class Set

{

   public:

   //各种接口

   .........

  private:

  std::list<T> rep;

};

条款39:明智而审慎的使用private继承

private继承是一种纯粹的实现技术,意思就是感觉某物实现的意思。他在软件设计的层面上是没有意义的。主要在软件实现层面上的意义。那么private继承与复合需要如何取舍呢?我们应该尽可能使用复合,必要的时候使用private继承。什么时候是必要的?主要是protected成员和virtual函数牵涉进来的时候。

当你面对"并不存在is-a关系"的两个类,其中一个类需要访问另外一个类的protected成员或者需要重新定义一个或多个virtual函数,private继承会是很好的选择。

条款40:明智而审慎的使用多继承

多继承的歧义:

  class Borrow

 {

    public:

     void check();

    ....

  };

class Elect

{

    private:

     bool check();

    ....

};

class MP3:public Borrow,public Elect

{

     .....

};

MP3 pm3;

mp3.check();// 歧义

我们可以指定基类:mp3.Borrow::check();

多重继承容易导致钻石形状,如C++的文件对象就是如此

class File{...};
class InputFile: public File {...};
class OutputFile: public File{...};
class IOFile: public InputFile,
                    public OutputFile
{...}; 

如果你想避免数据重复,可以采用virtual继承

class File{...}; 
class InputFile: virtual public File {...}; 
class OutputFile: virtual public File{...}; 
class IOFile: public InputFile, 
                    public OutputFile 
{...}; 

从正确行为来看,public继承应该总是virtual,用virtual继承的那些classes所产生的对象往往比使用non-virtual继承的兄弟们体积大,访问virtual base classes的成员变量时,也比访问non-virtual base classes成员变量速度慢。

下面看下C++ interface的例子:

class IPerson{
public:
    virtual ~IPerson();
    virtual std::string name() const = 0;
    virtual std::string birthDate() const =0;
};

//根据一个独一无二的数据库ID创建一个Person对象
std::tr1::shared_ptr<IPerson> makePerson(DatabaseID personIdentifier);
DatabaseID askUserForDatabaseID();
DatabaseID id(askUserForDatabaseID());
std::tr1::shared_ptr<IPerson> pp(makePerson(id));

CPerson继承自IPerson, 另外有个PersonInfo类提供CPerson的实质东西

class PersonInfo{ 

public: 

    explicit PersonInfo(DatabaseID pid);
    virtual ~PersonInfo();
    virtual const char* theName()const;
    virtual const char* theBirthDate() const;
private:
    virtual const char* valueDelimOpen() const;
    virtual const char* valueDelimClose() const;
};

PersonInfo被设计用来协助以各种格式打印数据库字段,每个字段值的起始点和结束点以特殊字符串为界。默认为“[”,“]”。但是不同的人有不同的需求,有时候我们不需要这些。所以我们定义了两个虚函数:

PersonInfo::theName的代码看起来像这样:

const char* PersonInfo::valueDelimOpen() const
{
    return "[";//default
}
const char* PersonInfo::valueDelimClose() const
{
    return "]";//default
}
const char* PersonInfo::theName() const
{
    //保留缓冲区给返回值使用:static,自动初始化为“全0”
    static char value[Max_Formatted_Field_Value_Length];
    //写入起始符号
    std::strcpy(value, valueDelimOpen());
    将value内的字符串附到这个对象的name成员变量中
    //写入结尾符号
    std::strcat(value, valueDelimClose());
    return value;
}

所以theName返回的结果不仅仅取决于PersonInfo也取决于从PersonInfo派生下去的classes。

Cperson和personInfo的关系是,PersonInfo刚好有若干函数可帮助Cperson比较容易实现出来。因此它们的关系是is-implemented-in-term-of。这种关系可以两种技术实现:复合和private继承。一般复合必要受欢迎,本例之中Cperson要重新定义valueDelimOpen和valueDelimClose,所以直接的解法是private继承。

Cperson还有必须实现Iperson的接口,那得要public继承才能完成。这导致多重继承的一个通情达理的应用:将“public继承自某接口”和“private继承自某实现”结合在一起:

class Cperson: public IPerson,private PersonInfo{
public:
    explicit Cperson(DatabaseID pid): PersonInfo(pid){}
    virtual std::string name() const
    {
        return PersonInfo::theName();
    }
    virtual std::string birthDate() const
    {
        return PersonInfo::theBirthDate();
    }
private:
    const char* valueDelimOpen() const {return "";}
    const char* valueDelimClose() const {return "";}
};

如果你唯一能提出的设计涉及多重继承,你应该再努力想一想----几乎可以说一定会有某些方案让单一继承行的通。然而有时候多继承的确是完成任务最简洁、最易维护、最合理的做法,就别害怕使用它。只是确定,的确在明智而审慎的情况下使用它。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值