effective C++笔记之条款41、42: 区分继承和模板、明智地使用私有继承

条款41:区分继承和模板

l        模板类的特点:行为不依赖于类型

l        当对象的类型不影响类中函数的行为时,就要使用模板来生成这样一组类。(堆栈类型)

因为堆栈类型中,即使你对堆栈中的成员类型T一无所知,还是能够写出每个成员函数,stack的行为在任何地方都不依赖于T。

l        当对象的类型影响类中的行为时,就要使用继承来得到这样一组类。(猫的类型)

因为每个猫吃和睡都有各自的方式。这意味着必须为每种不同的猫实现不同的行为。不可能写一个函数来处理所有的猫。所能做的只是制定一个函数接口,所有的猫必须实现     它。所以在基类中声明一个纯虚函数去制定接口。

 

条款42明智地使用私有继承

l        私有继承行为:第一、和公有继承相反,如果两个类之间的继承关系为私有,编译器一般不会将派生类对象转换成基类对象。第二、从私有基类继承而来的成员都成为了派生类的私有成员,即使他们在基类中是保护或公有成员

l        私有继承意味着“用 …来实现”。如果使用D私有继承于B,这样做是因为你想利用类B中已经存在的某些代码,而不是因为类型B的对象和类型D的对象之间有什么概念上的关系。因而,私有继承是一种实现技术。私有继承意味着只是继承实现,接口会被忽略。如果D私有继承于B,就是说D对象在实现中用到了B对象,仅此而已。私有继承在软件设计过程中毫无意义,只是在软件实现时才有用。

l        模板导致的“代码膨胀”不是件好事。如果实例化一个模板一百次,就可能实例化了那个模板的代码一百次。对于某些类可以采用通用指针(void *)来避免它。采用这种方法的类存储的是指针,而不是对象。如下所示的Stack类:

class GenericStack
{
public:
         GenericStack();
         ~GenericStack();
         void push(void *object);
         void *pop();
         boolempty() const;
private:
         structStackNode{
                   void*data;
                   StackNode*next;
                   StackNode(void*newData, StackNode *nextNode)
		: data(newData),next(nextNode) {}
                  };
	  StackNode *top;
};

为了获得类型安全,就要为GenericStack创建接口类如下所示:

         class IniStack
         {
         public:
                   void push(int *intPtr) { s.push(intPtr); }
                   int  *pop() { return static_cast<int*>(s.pop()); }
                   bool empty() const { return s.empty(); }
         private:
                   GenericStack s; //实现
         };

但是有些用户错误认为使用GenericStack更高效,或者他们认为类型安全不重要,而直接使用GenericStack,这会让他们很容易地犯类型错误。所以为了防止这样,通过私有继承类实现。通过它可以告诉别人:GenericStack使用起来不安全,它只能用来实现其他的类。具体做法是将GenericStack的成员函数声明为保护类型,如下所示:

class GenericStack
{
protected::
         GenericStack();
         ~GenericStack();
         void push(void *object);
         void *pop();
         bool empty() const;
private:
         struct StackNode{
                   void*data;
                   StackNode*next;
                   StackNode(void *newData, StackNode *nextNode)
		: data(newData),next(nextNode) {}
                  };
                   StackNode *top;
};

这时如果想直接使用GenericStack

         GenericStack s;  //错误!构造函数被保护
         class IntStack : private GenericStack
         {
         public:
                   void push(int *intPtr) {GenericStack::push(intPtr); }
                   int *pop() { return static_cast<int*>(GenericStack::pop()); }
                   bool empty() const { return GenericStack::empty();}
         };
和分层的方法一样,基于私有继承的实现 避免了代码重复 ,因为这个类型的安全的接口类只包含有对 GenericStack 函数的内联调用。

l        当类成员函数声明为protected时,只有通过继承才能访问保护成员,分层达不到同样的效果。因为存在虚函数(条款43中有详细说明)和保护成员,有时私有继承是表达类之间“用来实现”关系的唯一有效途径。

l        私有继承意味着“用来实现”,分层也具有相同的含义。在选择是应当尽可能地使用分层,必要时才使用私有继承(往往是指有保护成员/虚函数时)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值