《C++ Primer Plus》(第6版)中文版—学习笔记—C++中的代码重用

第14章 C++中的代码重用

公有继承、私有继承、保护继承,还有之前介绍的模板,都属于代码的重用。

包含对象成员的类

细看原文

私有继承

私有继承是一种实现has-a关系途径。使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法将不会称为派生对象公有接口的一部分,但可以在派生类的成员函数中使用它们。

公有继承:基类的公有方法将成为派生类的公有方法

私有继承:基类的公有方法将成为派生类的私有方法

新的Student类示例

class Student : private std::string, private std::valarray<double>
{
public:
	...
};

这里使用了多重继承,往往多重继承会引发一些问题,但是这里不会出现这种问题。

我们可以看到,新的Student类不需要私有数据,因为两个基类已经提供了所需的所有数据成员。包含版本提供了两个被显式命名的对象成员,而私有继承提供了两个无名称的子对象成员。这是两种方法的第一个主要区别。

  1. 初始化基类组件

    隐式地继承组件而不是对象成员将影响代码的编写,因为再也不能使用name和scores来描述对象了,而必须使用用于公有继承的技术。例如,对于构造函数,包含将使这样的构造函数:

    Student(const char * str, const double * pd, int n) : name(str), scores(pd, n) { };
    // use object names for containment
    

    对于继承类,新版本的构造函数将使用成员初始化列表语法,它使用类名而不是成员名来标识构造函数:

    Student(const char * str, const double * pd, int n) : std::string(str), ArrayDb(pd, n) { };
    // use class names for inheritance
    

    这就是第二个区别,可以看到新版本的成员初始化 列表使用std::string(str),而不是name(str)。

    可以看原文的例子

  2. 访问基类的方法

    我们要想使用基类的方法,只能在派生类的方法中使用基类的方法,并不能像公有继承一样直接使用了。

    但是新版本的Student类中的“name”和“scores”都是没有名称的,是std::string和std::valarray,那我们该如何访问呢?我们需要使用作用域解析运算符进行访问。

    double Student::Average() const
    {
          
          
    	if (scores.size) > 0
    		return scores.sum()/scores.size();
    	else
    		return 0;
    }
    
    // private inheritance
    double Student::Average() const
    {
          
          
    	if (scores.size) > 0
    		return ArrayDb::sum()/ArrayDb::size();
    	else
    		return 0;
    }
    
  3. 访问基类对象

    那么我们需要访问基类对象怎么办呢?答案是使用强制类型转换。由于Student派生于String类

    const string & Student::Name() const
    {
    	return (const string &) *this;
    }
    
  4. 访问基类的友元函数

    用类名显式地限定函数名不适合友元函数,这是因为友元不属于类。但是可以通过显式地转换为基类来调用正确的函数。

    ostream & operator<<(ostream & os, const Student & stu)
    {
    	os << "Scores for " << (const String &) stu << ":\n";
    	...
    }
    

保护继承

保护继承是私有继承的变体。保护继承在列出基类时使用关键字protected

class Student : protected std::string, protected std::valarray<double> {};

使用保护继承时,基类的公有成员和保护成员都将成为派生类的保护成员。和私有继承一样,基类的接口在派生类中也是可用的,但在继承层次结构之外是不可用的。当从派生类派生出另一个类时,私有继承和保护继承之间的主要区别变呈现出来了。使用私有继承时,第三代类将不能使用基类的接口,这是因为基类的公有方法在派生类中将变成私有方法;使用保护继承时,基类的公有方法在第二代中将变成受保护的,因此第三代派生类可以使用它们。

特征 公有继承 保护继承 私有继承
公有成员变成 派生类的公有成员 派生类的保护成员 派生类的私有成员
保护成员变成 派生类的保护成员 派生类的保护成员 派生类的私有成员
私有成员变成 只能通过基类接口访问 只能通过基类接口访问 只能通过基类接口访问
能否隐式向上转换 是(但只能在派生类中)

使用using重新定义访问权限

看原文

多重继承

必须使用关键字public进行限定每个基类。不然的话,编译器就会认为是私有派生

class SingerWaiter : public Singer, Waiter		// 这里Waiter就是私有继承

多重继承会引起一部分问题,加入基类和派生类之间的关系如下

Worker
Singer
Waiter
SingingWaiter

Worker是抽象基类,Singer和Waiter是派生于Worker的,而SingingWaiter又是派生于Singer和Waiter的

SingingWaiter ed;
Worker *pw = &ed;		// ambiguous

这样会产生二义性,因为SingingWaiter有两个Worker,所以需要下面这样写

Worker *pw1 = (Waiter *) &ed;
Worker *pw2 = (Waiter *) &ed;

但是c++提供了一个新技术,虚基类

class Singer : virtual public Worker {};
class Singer : public virtual Worker {};	// public和virtual顺序无关

类模板

// stacktp.h -- a stack template
#ifndef STACKTP_H_
#define STACKTP_H_
template <class Type>
class Stack
{
private:
    enum {MAX = 10};    // constant specific to class
    Type items[MAX];    // holds stack items
    int top;            // index for top stack item
public:
    Stack();
    bool isempty();
    bool isfull();
    bool push(const Type & item); // add item to stack
    bool pop(Type & item);        // pop top into item
};

template <class Type>
Stack<Type>::Stack()
{
    top = 0;
}

template <class Type>
bool Stack<Type>::isempty()
{
    return top == 0;
}

template <class Type>
bool Stack<Type>::isfull()
{
    return top == MAX;
}

template <class Type>
bool Stack<Type>::push(const Type & item)
{
    if (top < MAX)
    {
        items[top++] = item;
        return true;
    }
    else
        return false;
}

template <class Type>
bool Stack<Type>::pop(Type & item)
{
    if (top > 0)
    {
        item = items[--top];
        return true;
    }
    else
        return false; 
}

#endif

对于模板,指针可以吗?答案是可以创建指针栈。细看原文例子

猜你喜欢

转载自blog.csdn.net/weixin_49643423/article/details/114089990