继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段,它允许程序员在 保 持原有类特性的基础上进行扩展 ,增加功能,这样产生新的类,称派生类。继承 呈现了面向对象 程序设计的层次结构 ,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用, 继 承是类设计层次的复用。
一个简单的基类:
在一个类派生出另一个类的过程中,原始类我们称作基类,继承类称作派生类。下面我们使用一个例子来进行说明,首先我们要有一个基类Person,然后我们派生出一个Student学生类:
//父类、基类
class Person
{
public:
void Print()
{
cout << "name :" << _name << endl;
cout << "age :" << _age << endl;
}
//private:
//protected:
//这里是一个C++11的类内初始化特性!
string _name = "Peter";
int _age = 18;
};我们现在写一个学生类:
首先我们看到派生类的声明方式
//派生类的声明方式
class Student : public Person
{
public:
void func()
{
Person::Print();
}
protected:
int _stuId;
};派生类会继承基类的所有公共部分,同时基类的私有部分也会成为派生类的一部分,总的来说就是Student类定义的对象s,可以使用Person类中的公有方法
我们看到Student是继承自Person,同时添加了Student自己的成员变量和成员函数:

从sizeof的输出我们可以看出Student确实是包括了Person中的成员变量的,至于为什么是56(这个可能因为机器不同结果不同),感兴趣的同学可以研究一下。
上文我们提到,Person是基类,Student是派生类,继承方式是公有继承:

继承关系除了上面讲到的公有继承(public),还包括保护继承(protected)、私有继承(private),在加上基类成员本身的访问限定,所以组合之后会有9中访问关系。

类成员 / 继承方式 | public 继承 | protected 继承 | private 继承 |
|---|---|---|---|
基类的 public 成员 | 派生类的 public 成员 | 派生类的 protected 成员 | 派生类的 protected 成员 |
基类的 protected 成员 | 派生类的 protected成员 | 派生类的 protected 成员 | 派生类的 private 成员 |
基类的 private 成 员 | 在派生类中 不可见 | 在派生类中 不可见 | 在派生类中 不可见 |
总结:
下面举一个例子:
int main()
{
Student s;
s.Print();
s._age;
s._name;
s._ID;
return 0;
}


切片
class Person
{
//protected:
public:
string _name; // 姓名
string _sex; // 性别
int _age; // 年龄
};
class Student : public Person
{
public:
int _No; // 学号
};Person p;
Student s;
p = s; //对象
Person& rp = s; //引用
Person* ptrp = &s; //指针
Person p;
Student s;
p = s; //对象
Person& rp = s; //引用
Person* ptrp = &s; //指针
ptrp = &s;
Student* ptrs = (Student*)ptrp;
cout << ptrs->_No << endl;
ptrp = &p;
Student* ptrs2 = (Student*)ptrp;
cout << ptrs2->_No << endl; 大家可以看一下这段代码的最后两部分,我们都试图通过使用强制类型转换的方式将基类指针赋值给派生类,但是仔细阅读会发现还是有所区别,前置ptrp指向派生类,后者ptrp指向基类:

我们发现因为ptrp是指向派生类的,所以在进行派生类的强制类型转换的时候指针的结构并没有冲突。但是,后者ptrp是指向了基类,而此时继续进行派生类的强制类型转换,指针指向的布局并不符合Student类,所以这个时候会发上访问到无效的内存数据。
class Person
{
protected:
string _name = "张三"; // 姓名
int _num = 111; // 身份证号
};
class Student : public Person
{
public:
void Print()
{
cout << " 姓名:" << _name << endl;
cout << " 学号:" << _num << endl;//会使用自己的成员变量 999
cout << " 身份证号:" << Person::_num << endl; //要指定作用域
}
protected:
int _num = 999; // 学号
};
int main()
{
Student s;
s.Print();
return 0;
}
class A
{
public:
void fun()
{
cout << "A::func()" << endl;
}
};
class B : public A
{
public:
void fun()
{
cout << "B::func()" << endl;
}
};
int main()
{
B b;
b.fun();//b的fun()隐藏了A的fun()
b.A::fun();//指定作用域即可访问到A的func()
return 0;
};
我们在之前刚学习C++类和对象时,就提到了类会有6个默认成员函数,那么在派生类的影响下,会对这6个默认成员函数有什么影响呢?

class Person
{
public:
Person(const char* name)
:_name(name)
{
cout << "Person(const char* name)" << 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 = 0)
:_num(num)
,Person(name)
{
cout << "Student(const char* name = "", int num = 0)" << endl;
}
//拷贝构造
Student(const Student& s)
:Person(s)
,_num(s._num)
{
cout << "Student(const Student& s)" << endl;
}
//赋值构造
// s1 = s3
Student& operator=(const Student& s)
{
if (this != &s)
{
Person::operator=(s);
_num = s._num;
}
cout << "Student& operator=(const Student& s)" << endl;
return *this;
}
//父类和子类析构函数构成隐藏关系
//原因:多态的需要,析构函数名同意会被处理成destructor()
//为了保证析构的顺序,先子后父
//子类析构函数完成后会自动调用父类析构函数,所以不需要我们显示调用
~Student()
{
//父类的析构函数我们不需要显示调用
//Person::~Person();
cout << "~Student()" << endl;
}
protected:
int _num; //学号
};
class Base {
friend void friendOfBase(Base& b);
private:
int basePrivate;
};
class Derived : public Base {
private:
int derivedPrivate;
};
void friendOfBase(Base& b) {
b.basePrivate = 10; // 合法(Base的友元)
// 无法访问 Derived::derivedPrivate(即使参数是Derived对象)
}class Derived : public Base {
friend void friendOfDerived(Derived& d);
private:
int derivedPrivate;
};
void friendOfDerived(Derived& d) {
d.derivedPrivate = 20; // 合法(Derived的友元)
// d.basePrivate = 30; // 错误!无法访问基类的私有成员
}class Base {
friend void friendOfDerived(Derived& d); // 显式声明
private:
int basePrivate;
};
class Derived : public Base {
friend void friendOfDerived(Derived& d);
private:
int derivedPrivate;
};
void friendOfDerived(Derived& d) {
d.derivedPrivate = 20; // 合法(Derived的友元)
d.basePrivate = 30; // 合法(现在是Base的友元)
}当基类中定义了一个static静态成员,无论派生了多少个子类,这个静态成员都只有一个:
class Person
{
public:
Person() { ++_count; }
protected:
string _name; // 姓名
public:
static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum; // 学号
};
class Graduate : public Student
{
protected:
string _seminarCourse; // 研究科目
};
//
int main()
{
Student s1;
Student s2;
Student s3;
Graduate s4;
Person s;
cout << " 人数 :" << Person::_count << endl;
cout << " 人数 :" << Student::_count << endl;
cout << " 人数 :" << s4._count << endl;
//同一个地址
cout << " 人数 :" << &Person::_count << endl;
cout << " 人数 :" << &Student::_count << endl;
cout << " 人数 :" << &s4._count << endl;
return 0;
}
我们可以看出不管是从值还是地址的角度,基类中的静态成员都只有一个!



从上面的图中可以看出,因为Assistant同时继承了Student和Teacher,所以就会导致有两份Person成员:
#include <iostream>
using namespace std;
class Person
{
public:
string _name; // 姓名
int _a[10000]; //会有多份_a
};
class Student : Person
{
protected:
int _num; //学号
};
class Teacher : Person
{
protected:
int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse; // 主修课程
};
int main()
{
Assistant asst;
asst._name = "张三"; //只有一份_name
cout << asst._name << endl;
return 0;
}
因为两份Person成员的存在,所以访问_name的时候就会出现二义性,不知道是哪一个。
虚拟继承可以解决二义性指向不明以及数据冗余的问题,就是在Student和Teacher继承Person时候进行虚拟继承:
//继承的时候使用vittual关键字
class Student : virtual public Person
{
protected:
int _num; //学号
};
class Teacher : virtual public Person
{
protected:
int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse; // 主修课程
};(本篇完)