继承的基本概念与语法
单继承时派生类的定义
语法
class 派生类名:继承方式 基类名 {
成员声明;
}
class Derived: public Base
{
public:Derived();~Derived();
};
多继承时派生类的定义
语法
class 派生类名:继承方式1 基类名1,继承方式2 基类名2,... {
成员声明;
}
注意:每一个“继承方式”,只用于限制对紧随其后之基类的继承。
class Derived: public Base1, private Base2 {
public: Derived(); ~Derived();
};
派生类的构成
吸收基类成员
- 默认情况下派生类包含了全部基类中除构造和析构函数之外的所有成员。
- C++11规定可以用using语句继承基类构造函数。
改造基类成员
- 如果派生类声明了一个和某基类成员同名的新成员,派生的新成员就隐藏或覆盖了外层同名成员。
添加新的成员
- 派生类增加新成员使派生类在功能上有所发展。
不同继承方式的影响主要体现:
- 派生类成员对基类成员的访问权限
- 通过派生类对象对基类成员的访问权限
公有继承
继承的访问控制
- 基类的public和protected成员:访问属性在派生类中保持不变;
- 基类的private成员:不可直接访问。
访问权限
- 派生类中的成员函数:可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
- 通过派生类的对象:只能访问public成员。
例 7-1 公有继承举例
//Point.h
#ifndef _POINT_H
#define _POINT_H
class Point { //基类Point类的定义
public: //公有函数成员 void initPoint(float x = 0, float y = 0) { this->x = x; this->y = y;} void move(float offX, float offY) { x += offX; y += offY; } float getX() const { return x; } float getY() const { return y; }
private: //私有数据成员 float x, y;
};
#endif //_POINT_H
//Rectangle.h
#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#include "Point.h"
class Rectangle: public Point { //派生类定义部分
public: //新增公有函数成员 void initRectangle(float x, float y, float w, float h) { initPoint(x, y); //调用基类公有成员函数 this->w = w; this->h = h; } float getH() const { return h; } float getW() const { return w; }
private: //新增私有数据成员 float w, h;
};
#endif //_RECTANGLE_H
#include <iostream>
#include <cmath>
using namespace std;
#include “Rectangle.h”
int main() { Rectangle rect; //定义Rectangle类的对象 //设置矩形的数据 rect.initRectangle(2, 3, 20, 10); rect.move(3,2); //移动矩形位置 cout << "The data of rect(x,y,w,h): " << endl; //输出矩形的特征参数 cout << rect.getX() <<", " << rect.getY() << ", " << rect.getW() << ", " << rect.getH() << endl; return 0;
}
私有继承
继承的访问控制
- 基类的public和protected成员:都以private身份出现在派生类中;
- 基类的private成员:不可直接访问。
访问权限
- 派生类中的成员函数:可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
- 通过派生类的对象:不能直接访问从基类继承的任何成员。
例 7-2 私有继承举例
//Point.h
#ifndef _POINT_H
#define _POINT_H
class Point { //基类Point类的定义
public: //公有函数成员 void initPoint(float x = 0, float y = 0) { this->x = x; this->y = y;} void move(float offX, float offY) { x += offX; y += offY; } float getX() const { return x; } float getY() const { return y; }
private: //私有数据成员 float x, y;
};
#endif //_POINT_H //Rectangle.h
#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#include "Point.h"
class Rectangle: private Point { //派生类定义部分
public: //新增公有函数成员 void initRectangle(float x, float y, float w, float h) { initPoint(x, y); //调用基类公有成员函数 this->w = w; this->h = h; } void move(float offX, float offY) { Point::move(offX, offY); } //调用基类成员函数,使得可以通过对象调用float getX() const { return Point::getX(); } float getY() const { return Point::getY(); } float getH() const { return h; } float getW() const { return w; }
private: //新增私有数据成员 float w, h;
};
#endif //_RECTANGLE_H #include <iostream>
#include <cmath>
using namespace std;
int main() {
Rectangle rect; //定义Rectangle类的对象
rect.initRectangle(2, 3, 20, 10); //设置矩形的数据 rect.move(3,2); //移动矩形位置 cout << "The data of rect(x,y,w,h): " << endl; cout << rect.getX() <<", " //输出矩形的特征参数 << rect.getY() << ", " << rect.getW() << ", " << rect.getH() << endl; return 0;
}
保护继承
继承的访问控制
- 基类的public和protected成员:都以protected身份出现在派生类中;
- 基类的private成员:不可直接访问。
访问权限
- 派生类中的成员函数:可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
- 通过派生类的对象:不能直接访问从基类继承的任何成员。
多继承举例 略
基类与派生类类型转换
类型转换
公有派生类对象可以被当做基类的对象使用,反之则不可。
- 派生类的对象可以隐含转换为基类对象;
- 派生类的对象可以初始化基类的引用;
- 派生类的指针可以隐含转换为基类的指针。
通过基类对象名、指针只能使用从基类继承的成员
//例7-3类型转换规则举例
#include <iostream>
using namespace std;
class Base1 { //基类Base1定义
public:void display() const {cout << "Base1::display()" << endl;}
};
class Base2: public Base1 { //公有派生类Base2定义
public:void display() const {cout << "Base2::display()" << endl;}
};
class Derived: public Base2 { //公有派生类Derived定义
public:void display() const {cout << "Derived::display()" << endl;}
};
void fun(Base1 *ptr) { //参数为指向基类对象的指针ptr->display(); //"对象指针->成员名"
}
int main() { //主函数Base1 base1; //声明Base1类对象Base2 base2; //声明Base2类对象Derived derived; //声明Derived类对象fun(&base1); //用Base1对象的指针调用fun函数 fun(&base2); //用Base2对象的指针调用fun函数 调用的还是Base1里面的display函数fun(&derived); //用Derived对象的指针调用fun函数 调用的还是Base1里面的display函数return 0;
}
结论:不要重新定义继承而来的非虚函数。
解决办法:第八章虚函数讲解