继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称子类。 继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用,继承是类设计层次的复用。
class Student
{
public :
// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证
void identity()
{
// ...
}
// 学习
void study()
{
// ...
}
protected:
string _name = "peter"; // 姓名
string _address; // 地址
string _tel; // 电话
int _age = 18; // 年龄
int _stuid; // 学号
};
class Teacher
{
public:
// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证
void identity()
{
// ...
}
// 授课
void teaching()
{
//...
}
protected:
string _name = "张三"; // 姓名
int _age = 18; // 年龄
string _address; // 地址
string _tel; // 电话
string _title; // 职称
};
上面的Student
和Teacher
中成员函数和成员变量有相同的地方,下面我们将公共的部分放到Person
中,然后让Student
和Teacher
都继承Person
,就可以复用这些成员,使代码不再冗杂。
class Person
{
public:
// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证
void identity()
{
// ...
}
protected:
string _name = "peter"; // 姓名
string _address; // 地址
string _tel; // 电话
int _age = 18; // 年龄
};
class Student : public Person
{
public :
// 学习
void study()
{
// ...
}
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
public:
// 授课
void teaching()
{
//...
}
protected:
string _title; // 职称
};
Person
是父类,也称基类;Student
是子类,也称派生类。
类成员 / 继承方式 | public继承 | protected继承 | private继承 |
---|---|---|---|
父类的public成员 | 子类的public成员 | 子类的protected成员 | 子类的private成员 |
父类的protected成员 | 子类的protected成员 | 子类的protected成员 | 子类的private成员 |
父类的private成员 | 子类中不可见 | 子类中不可见 | 子类中不可见 |
private
成员在子类中无论以什么方式继承都是不可见的。不可见是指父类的私有成员还是被继承到了子类中,但是语法上限制子类对象不管在类里面还是类外面都不能去访问它private
成员在子类中是不能被访问的,如果父类成员不想在类外直接被访问,但需要在子类中能访问,就定义为protected
。可以看出保护成员限定符是因继承才出现的class
时默认的继承方式是private
,使用struct
时默认的继承方式是public
,不过最好显示的写出继承方式public
继承,几乎很少使用protetced/private
继承,也不提倡使用protetced/private继承,因为protetced/private
继承下来的成员都只能在子类的类里面使用,实际中扩展维护性不强public
继承的子类对象可以赋值给父类的对象 / 父类的指针 / 父类的引用。有个形象的说法叫切片或者切割。寓意把子类中父类那部分切来赋值过去class Person
{
protected:
string _name;
string _sex;
size_t _age;
};
class Student : public Person
{
public:
int _no;//学号
};
int main()
{
Student s;
Person p = s;
Person* pp = &s;
Person& rp = s;
return 0;
}
class A
{
public :
void fun()
{
cout << "func()" << endl;
}
};
class B : public A
{
public :
void fun(int i)
{
cout << "func(int i)" << i << endl;
}
};
int main()
{
B b;
b.fun(10);
b.fun();
return 0;
};
fun
函数构成的是隐藏关系,不是重载fun
函数被隐藏,B类中的fun
函数形参没有缺省值,所以主函数中的代码b.fun()
会报错,如果想要调用A类中的fun
函数,需要指定作用域:b.A::fun();
operator=
隐藏了父类的operator=
,所以显示调用父类的operator=
需要指定父类作用域destructor()
,所以父类析构函数不加virtual
的情况下,子类析构函数和父类析构函数构成隐藏关系#include <iostream>
using namespace std;
class Person
{
public:
Person(const char* name = "peter")
:_name(name)
{
cout << "Person()" << endl;
}
Person(const Person& p)
: _name(p._name)
{
cout << "Person(const Person& p)" << endl;
}
Person& operator=(const Person& p)
{
cout << "Person operator=(const Person& p)" << endl;
if (this != &p)
_name = p._name;
return *this;
}
~Person()
{
cout << "~Person()" << endl;
}
protected:
string _name; // 姓名
};
class Student : public Person
{
public :
Student(const char* name, int num)
: Person(name)
, _num(num)
{
cout << "Student()" << endl;
}
Student(const Student& s)
: Person(s)
, _num(s._num)
{
cout << "Student(const Student& s)" << endl;
}
Student& operator=(const Student& s)
{
cout << "Student& operator=(const Student& s)" << endl;
if (this != &s)
{
// 构成隐藏,所以需要显示调用
Person::operator=(s);
_num = s._num;
}
return *this;
}
~Student()
{
//子类的析构和父类的析构也构成隐藏关系,如果调用需要指定类域
//但是子类析构后会自动调用父类析构,所以不需要显示调用
//Person::~Person();
cout << "~Student()" << endl;
}
protected:
int _num; //学号
};
int main()
{
Student s1("jack", 18);
Student s2(s1);
Student s3("rose", 17);
s1 = s3;
return 0;
}
final
关键字,final
修饰父类,子类就不能继承了// C++11的⽅法
class Base final
{
public:
void func5() { cout << "Base::func5" << endl; }
protected:
int a = 1;
private:
// C++98的⽅法
/*Base()
{}*/
};
class Derive : public Base
{
void func4() { cout << "Derive::func4" << endl; }
protected:
int b = 2;
};
int main()
{
Base b;
Derive d;
return 0;
}
//前置声明
class Student;
class Person
{
public :
friend void Display(const Person& p, const Student& s);
protected:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
cout << p._name << endl;
cout << s._stuNum << endl;
}
int main()
{
Person p;
Student s;
// 编译报错:error C2248: “Student::_stuNum”: ⽆法访问 protected 成员
// 解决⽅案:Display也变成Student 的友元即可
Display(p, s);
return 0;
}
class Person
{
public:
string _name;
static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum;
};
int main()
{
Person p;
Student s;
// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的
// 说明⼦类继承下来了,⽗⼦类对象各有⼀份
cout << &p._name << endl;
cout << &s._name << endl;
// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的
// 说明⼦类和⽗类共⽤同⼀份静态成员
cout << &p._count << endl;
cout << &s._count << endl;
// 公有的情况下,⽗⼦类指定类域都可以访问静态成员
cout << Person::_count << endl;
cout << Student::_count << endl;
return 0;
}
单继承:
多继承:
菱形继承:
class Person
{
public :
string _name; // 姓名
};
class Student : public Person
{
protected :
int _num; //学号
};
class Teacher : public Person
{
protected :
int _id; // 职⼯编号
};
class Assistant : public Student, public Teacher
{
protected :
string _majorCourse; // 主修课程
};
int main()
{
// 编译报错:error C2385: 对“_name”的访问不明确
Assistant a;
a._name = "peter";
// 需要显⽰指定访问哪个⽗类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
return 0;
}
virtual
virtual
class Person
{
public :
string _name; // 姓名
};
// 使⽤虚继承Person类
class Student : virtual public Person
{
protected :
int _num; //学号
};
// 使⽤虚继承Person类
class Teacher : virtual public Person
{
protected:
int _id; // 职⼯编号
};
// 教授助理
class Assistant : public Student, public Teacher
{
protected :
string _majorCourse; // 主修课程
};
int main()
{
// 使⽤虚继承,可以解决数据冗余和⼆义性
Assistant a;
a._name = "peter";
return 0;
}
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main()
{
Derive d;
Base1* p1 = &d;
Base2* p2 = &d;
Derive* p3 = &d;
return 0;
}
p1 == p3 != p2
public
继承是一种is-a
的关系,也就是说每个子类对象都是一个父类对象has-a
的关系,如果B组合了A,则每个B对象中都有一个A对象// Tire(轮胎)和Car(⻋)更符合has-a的关系
class Tire
{
protected:
string _brand = "Michelin"; // 品牌
size_t _size = 17; // 尺⼨
};
class Car
{
protected:
string _colour = "⽩⾊"; // 颜⾊
string _num = "陕ABIT00"; // ⻋牌号
Tire _t1; // 轮胎
Tire _t2; // 轮胎
Tire _t3; // 轮胎
Tire _t4; // 轮胎
};
class BMW : public Car
{
public:
void Drive() { cout << "好开-操控" << endl; }
};
// Car和BMW/Benz更符合is-a的关系
class Benz : public Car {
public:
void Drive() { cout << "好坐-舒适" << endl; }
};
// stack和vector的关系,既符合is-a,也符合has-a
template<class T>
class vector
{};
template<class T>
class stack : public vector<T>
{};
template<class T>
class stack
{
public :
vector<T> _v;
};
int main()
{
return 0;
}