一、继承
1.概念
- 继承是利用已有的类定义新的类,被继承的为基类(父类),新类为派生类(子类),他们是is-a关系
- 一个基类可以有多个派生类
- 新类可以作为基类继续派生,形成类族
- 直接基类、间接基类
- 从一个类派生——单继承,有一个直接基类
- 从多个类派生——多重继承,有多个直接基类
2.派生类
- 继承基类的数据成员、成员函数
- 产生新成员:新数据成员、新成员函数
- 重新定义已有成员函数
- 改变已有成员的访问属性
3.单继承
(1)定义
class 派生类名:继承方式 基类名 {派生类中的新成员}
(2)继承方式
public公有继承,protected保护继承,private私有继承(默认)
(3)成员构成
- 继承基类的数据成员、成员函数,不包括构造和析构函数
- 定义同名的成员来覆盖基类的数据成员和成员函数
- 添加新的数据成员、成员函数
(4)访问属性
- 基类的private和protected成员在派生类中不可访问
- 公有继承:基类的public和protected成员在派生类中属性不变
- 保护继承:基类的public和protected成员在派生类中有protected属性
- 私有继承:基类的public和protected成员在派生类中有private属性
(5)派生类的构造函数
- 定义:派生类构造函数名(总参数表):
基类构造函数名(参数表) {派生类中新增数据成员初始化语句}
。总参数表包括基类构造函数和新增数据成员初始化所需参数 - 先调用基类的构造函数,初始化基类的数据成员,再执行派生类的构造函数初始化新增数据成员
- 若无显式调用,则自动生成一个对基类的默认构造函数的调用
- 显式调用只能在派生类构造函数的初始化成员列表中进行,可以调用基类中不带参数的默认构造函数,也可以调用合适的带参数的其他构造函数
- 先执行派生类析构函数,再执行编译器自动调用的基类的析构函数
- 在派生类的class中用
using 基类名::基类名
=基类定义的构造函数 - 基类构造函数的参数默认值不会被派生类继承,但由默认参数导致的多个构造函数版本都会被派生类继承
- 如果基类的某个构造函数被声明为私有成员函数,则不能在派生类中声明继承该构造函数
- 如果派生类使用了继承基类构造函数,编译器就不会再为派生类生成默认构造函数
#include<iostream>
using namespace std;
class Shape {
protected:
int x, y;
public:
Shape();
Shape(int x, int y);
~Shape();
};
Shape::Shape() {
x = y = 0;
cout<<"Shape:constructor Shape()\n";
}
Shape::Shape(int x, int y) {
this->x = x;
this->y = y;
cout<<"Shape:constructor Shape(int, int)\n";
}
Shape::~Shape() {
cout<<"shape:destructor\n";
}
class Circle:public Shape {
private:
int r;
public:
Circle(int x);
Circle(int x, int y, int r);
void Show();
~Circle();
};
Circle::Circle(int r) {
this->r = r;
cout<<"Circlr:constructor Circle(int)\n";
}
Circle::Circle(int x, int y, int r):Shape(x, y) {//调用 Shape 类的构造函数
this->r = r;
cout<<"Circlr:constructor Circle(int, int, int)\n";
}
Circle::~Circle() {
cout<<"Circle:destructor\n";
}
void Circle::Show() {
cout<<"A circle:";
cout<<"("<<x<<","<<y<<")";
cout<<","<<r<<endl;
}
int main() {
Circle c1(2), c2(1, 1, 3);
c1.Show();
c2.Show();
return 0;
}
//输出:
//Shape:constructor Shape()
//Circlr:constructor Circle(int)
//Shape:constructor Shape(int, int)
//Circlr:constructor Circle(int, int, int)
//A circle:(0,0),2
//A circle:(1,1),3
//Circle:destructor
//shape:destructor
//Circle:destructor
//shape:destructor
(6)基类包含成员对象的派生类(代码、示例没看懂)
- 派生类对象包含从基类继承来的数据成员,它们构成了“基类子对象”
- 基类中的私有成员,不允许在派生类成员函数中被访问,也不允许派生类的对象访问它们
- 基类中的公有成员:
- 若是使用public继承方式,则成为派生类的公有成员,既可以在派生类成员函数中访问,也可以被派生类的对象访问
- 若是使用private继承方式,则只能供派生类成员函数访问,不能被派生类的对象访问
(7)派生类重写基类成员函数(示例没看懂)
- 重写发生时,基类中该成员函数的其他重载函数都将被屏蔽掉(就好像不存在一样),不能提供给派生类对象使用
- 可以在派生类中通过
using 类名::成员函数名;
在派生类中“恢复”指定的基类成员函数(即去掉屏蔽),使之重新可用
4.多重继承(例子没看懂)
(1)定义:class 派生类名 :继承方式 基类名1,继承方式基类名2,{};
(2)派生类负责所有基类构造函数的调用,派生类名(总参数表):基类1名(参数表),基类2名(参数表) {派生类中新增的数据成员初始化语句};
(3)二义性:两种情况,①多个基类中有同名的成员,在派生类中访问同名成员时;②多个基类有一个共同的基类(祖先类),在派生类中访问祖先类的成员时。
(4)二义性解决方法:①使用限定名来访问基类的同名成员;②将祖先类设置为虚基类
5.赋值兼容原则
- 在公有继承的情况下,一个派生类的对象可以作为基类对象来使用
- 派生类的对象可以赋值给基类对象
- 派生类的对象可以赋值给基类的引用
- 派生类对象的地址可以赋值给指向基类对象的指针变量
void main() {
Shape s(1, 1), &rs = s, *ps = &s;
Circle c(3, 2, 2), &rc = c, *pc = &c;
Shape sc = c, &rsc = c, *psc = &c;
}
#include <iostream>
using namespace std;
void MoveTo(Shape &s, int x, int y) {
cout<<"Move "; s.Show();
s.SetPos(x, y);
cout<<"to (" <<x<<","<<y<<")\n";
}
void main() {
Shape s(1, 1);
Circle c(3, 2, 2);
MoveTo(s, 5, 5);
cout<<"Now, "; s.Show(); cout<<endl;
MoveTo(c, 8, 8);
cout<<"Now, "; c.Show(); cout<<endl;
}
6.联编
- 在编译时或运行时确定函数调用所使用的函数体(代码)
- 把函数名与其实现代码联系起来。把一个消息与一个方法联系起来
- 根据实现阶段的不同分为:静态联编(编译时完成)早期联编、前联编;动态联编(运行时完成)晚期联编、后联编
(1)静态联编(没看懂
(2)虚函数(例子没
- 声明:
virtual 类型 函数名(参数表);
虚函数声明只能出现在类声明中的成员函数原型声明中 - 只有类的普通成员函数才能声明为虚函数,全局函数及静态成员函数不能声明为虚函数。
- 虚函数可以在一个或多个派生类中被重复定义,因此它属于函数重载的情况。但是,这种函数重载与一般的函数重载是不同的
- 虚函数在派生类中重新定义时必须与基类中的原型完全相同(函数名、参数个数、参数类型、参数顺序、返回值类型)
(3)多态性
- 同样的消息(调用同名成员函数)被类的不同对象接收时导致完全不同的行为
- 通过指向派生类对象的基类指针来调用虚函数的重载函数,实现运行时的多态性
7.纯虚函数
- 声明:
virtual 函数原型 =0;
- 没有函数体
8.抽象类
- 包含纯虚函数的类
- 主要作用:为一个类族建立一个公共的接口,使它们能更有效的发挥多态性
- 抽象类只能用作基类来派生新类,不能声明抽象类的对象,但可以声明抽象类的指针变量和引用变量
- 抽象类中可以定义纯虚函数和普通函数,如果抽象类的派生类没有定义基类中的纯虚函数,则必须再将该函数声明为纯虚函数,那么此派生类也是一个抽象类。
#include <iostream>
using namespace std;
class Shape {
public:
virtual void Show() = 0;
virtual double Area() = 0;
};
class Circle:public Shape {
private:
double r;
public:
Circle(double nr = 0) {
r = nr;
}
void Show();
double Area();
};
void Circle::Show() {
cout<<"Circle("<<r<<")";
}
double Circle::Area() {
return 3.14*r*r;
}
class Rectangle:public Shape {
private:
double l, w;
public:
Rectangle(double nl = 0, double nw = 0) {
l = nl;
w = nw;
}
void Show();
double Area();
};
void Rectangle::Show() {
cout<<"Rectangle("<<1<<","<<w<<")";
}
double Rectangle::Area() {
return l*w;
}
void ShowShape(Shape *ps) {
ps->Show();
}
double ShapeArea(Shape *ps) {
return ps->Area();
}
int main() {
Circle c(2.7);
ShowShape(&c);
cout<<"area is"<<ShapeArea(&c)<<endl;
Rectangle r(5.3, 6.2);
ShowShape(&r);
cout<<"area is"<<ShapeArea(&r)<<endl;
return 0;
}
//输出
//Circle(2.7)area is22.8906
//Rectangle(1,6.2)area is32.86