最近在看 C++,再细读关于面向对象的一些知识点,好记性不如烂笔头,就归类总结一下。
面向对象中,继承是非常重要的基础概念。从已有的类派生出新的类,就可对原有的类进行扩展和修改。
称已有的类为基类,继承出来的类叫做派生类。
什么时候使用继承呢? 如果两个对象是is-a关系,则可以用继承。
比如水果与苹果。苹果 (is-a) 水果,这时候 水果是基类,苹果则是水果的派生类。
如果不是(is-a)关系,最好不要用继承。比如下面的关系例子。
has-a 午餐 (has-a) 水果,但是午餐 !(is- a )水果。所以不合适继承
is-like-a 敌人(is-like-a)豺狼,但是敌人 !(is- a )豺狼。所以不适合继承。
is-implemented-as-a 数组(is-implemented-as-a)堆栈,但是 数组 !(is- a )堆栈。所以不适合继承。
uses-a 计算机(uses-a)打印机,但是 计算机 !(is- a )打印机。所以不适合继承。
继承实现很简单,但是要管理好继承类之间的关系,却是件比较复杂的事情。
涉及到构造析构函数,虚函数&虚函数表,静态联编&动态联编,抽象基类,动态内存分配等。
如果不了解这些机制,设计的类之间就会出现一些和自己原有意图违背的运行错误,以及内存泄漏。
- 基础实现
继承的基本实现非常简单。语法就是定义类时,定义类class MyClass的后面 接上": 修饰符 基类名"即可。
修饰符有 public, private, protect 3种。这3个修饰符分别表示了C++的3种继承方式:公有继承,私有继承,保护继承。
比如SimpleClass继承了BaseClass, 「class SimpleClass : public BaseClass」并且修饰符为public,表明是公有继承。
公有继承也是最常用的方式。
关于修饰符,遵循一个基本规则,不论哪种继承方式,基类的私有成员,私有函数在派生类中都是不可见的。
想访问他们,只能通过基类提供公有或者保护方法去访问。
通过3种继承方式,原有基类的成员访问属性也会发生变化。
公有继承:派生类公有继承基类后,基类的成员和函数的访问限制在派生类中不变。
基类原来是public,到了派生类里还是public。
私有继承:派生类私有继承基类后,基类的成员和函数的访问限制在派生类全部变成私有。
所以如果私有继承一个基类,就相当于完全隐藏了基类中的所有成员和函数。
保护继承:派生类私有继承基类后,基类的成员和函数的public访问限制在派生类全部变成protect。
基类中的private还是继续保持private。
关于protect属性的注意点,类数据成员一般推荐用private,而不用protect或者public。
理由是用protect的话,派生类可以直接访问修改基类的数据成员。
成员函数用protect限定比较有用,使此成员函数只对派生类公开,对公众保持隐秘。
- 构造函数与析构函数
派生类需要有自己的构造函数,如果你没有看到构造函数,那只是编译器悄悄用默认的构造函数了。
派生类一定会调用基类的构造函数! 那么调用基类的那个构造函数呢? 答案很简单,你指定哪个就是哪个,如果你太懒不指定,那就调用默认的构造函数。
那如何指定呢?用成员初始化列表句法即可。
比如有一个艺术家类,记录艺术家的姓名,年龄。艺术家类派生出一个画家类。
画家除了有艺术家的姓名,年龄的基本属性外,还设定一个画作类别属性,表示画家最擅长的画是油画,中国画,水彩画之中的某一种。
#ifndef __CJiaJia__Artist__ #define __CJiaJia__Artist__ #include <iostream>
using namespace std; enum {LIM = 20}; class Artist { private: char firstname[LIM]; //所有艺术家都应该有一个姓名 char lastname[LIM]; unsigned int age; //所有艺术家都有自己的年龄 public: Artist (const char *fn = "none", const char *ln = "none", unsigned int age = 5); //构造函数 void showName() const; //显示艺术家的姓名 int getAge() const { return age; }; //获得艺术家的年龄 void setAge(int age) { this->age = age; }; //设定艺术家的年龄 }; class Painter : public Artist { private: char category[LIM]; //显示画家的画作种类。如水墨画,油画,水彩画等 public: Painter(const char *ct, const char *fn, const char *ln, unsigned int age); //构造函数1 Painter(const char *ct, const Artist & art); //构造函数2 char getCategory() const; //取得画家的绘画种类 void setCategory(const char *ct); //设定画家的绘画种类 };
#endif /* defined(__CJiaJia__Artist__) */
#include "Artist.h" Artist::Artist (const char *fn, const char *ln, unsigned int ag) { strncpy(firstname, fn, LIM - 1); firstname[LIM - 1] = '