谈谈继承跟组合(成员对象)这回事
在这里举个例子,由链表做一个栈。
class MyList;
其成员函数有:
void insert(int data, int position);//按位置插入
void delete_position(int position);//按位置删除
int get_size();//获得当前链表长度
int get_data(int position);//获得位置的值
void print(bool seq);//打印链表,seq为true时正序打印,false时倒序打印
先是由链表继承构建栈。
class MyStack:private MyList{
public:
void push(int data){
MyList::insert(data,1); //表头插入data
}
int pop() {
int data = MyList::get_data(1); //获得表头
MyList::delete_position(1); //删除表头
return data;
}
int get_size() {
return MyList::get_size(); //获得长度
}
void print() {
MyList::print(false); //倒序打印(从栈底打印至栈顶)
}
};
然后是由成员链表对象构建栈。
class MyStack2 {
private:
MyList l;//成员链表对象
public:
void push(int data) {
l.insert(data, 1);
}
int pop() {
int data = l.get_data(1);
l.delete_position(1);
return data;
}
int get_size() {
return l.get_size();
}
void print() {
l.print(false);
}
};
两者的作用都是一样的,那继承跟成员对象有什么区别呢?
我们回看程序,在继承构建的时候,成员函数get_size()只是在传递接口(因为继承方式为私有继承,链表中的get_size()接口被隐藏)
int get_size() {
return MyList::get_size(); //获得长度
}
所以,如果我们将继承方式改为公有继承,那我们就不需要手动提供(传递)这个接口,就可以直接调用。
class MyStack:public MyList{
......
};
int main(){
MyStack s;
......
s.get_size(); //直接调用MyList中的get_size(),不用在MyLis中手动提供
}
由此我们可以看出,继承方式下,对基类的接口可以直接调用,所以复用代码少些,但是子类跟父类耦合程度高。
而在成员链表对象中,我们只能手动传递接口,比继承实现需要的代码多,但是与原类耦合程度低,也不会破坏原类的封装。
换句话说,
继承,是 is a,我(子类)是父类的一种,我(子类)本身就有父类的功能。
成员对象,是 has a,我拥有这样一个成员对象,我让这个对象成员为我实现这个功能,所以我需要主动跟大家说我有这个功能(也即手动传递所有接口)。
两者各有优劣,但是在编程实现的时候,我比较倾向于后者,我认为将原对象作为一个成员对象,既保持了原对象的封装性跟接口性,而且在新对象中也可以对一些接口做屏蔽、处理。在上面程序里,对栈stack而言,打印时从栈底打印到栈顶,所以需要逆序打印,这样从原类提供的接口print(bool seq),修改为print(),也即对接口进行简单处理(因为这里不需要用到seq=true的情况)。
当然,在继承方式下,虚函数也可以实现,而且继承方式下的另一个好处就是可以用基类指针来引用来实现多态,这样在后续修改的时候就不用再去修改原程序调用。一般在向上转型的时候采用继承。