一、继承的概念及定义
1、继承的概念
// 基类
class Person
{
protected:
void print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "张三"; // 姓名
int _age = 18; // 年龄
};
// 派生类
class Student : public Person
{
protected:
int _stuid; // 学号
};
// 派生类
class Teacher : public Person
{
protected:
int _jobid; // 工号
};
int main()
{
Person p;
p.Print();
Student s;
s.print();
Teacher t;
t.print();
return 0;
}
2、继承定义
(1)定义格式
(2)继承关系和访问限定符
(3)继承基类成员访问方式的变化
下面这个表格不需要记忆,通过理解加深印象即可。
(红色框住的是常用的)
实例演示三种继承关系下基类成员的各类型成员访问关系的变化:
// 基类
class Person
{
public :
void Print ()
{
cout << _name << endl;
}
protected :
string _name ; // 姓名
private :
int _age ; // 年龄
};
// 派生类
//class Student : protected Person
//class Student : private Person
class Student : public Person
{
protected :
int _stunum ; // 学号
};
⚪【补充】
二、基类和派生类对象赋值转换
// 基类
class Person
{
protected :
string _name; // 姓名
string _sex; // 性别
int _age; // 年龄
};
// 派生类
class Student : public Person
{
public :
int _id; // 学号
};
void Test ()
{
Student sobj ;
// 1.子类对象可以赋值给父类对象/指针/引用
Person pobj = sobj ;
Person* pp =
Person sobj;
// 2.基类对象不能赋值给派生类对象
sobj = pobj;
// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
pp = &sobj
Student* ps1 = (Student*)pp; // 这种情况转换是可以的
ps1->_No = 10;
pp =
Student* ps2 = (Student*)pp; // 这种情况转换虽然可以,但是会存在越界访问的问题
ps2->_No = 10;
}
三、继承中的作用域
// Student的_num和Person的_num构成隐藏关系,虽然这样的代码虽然能跑,但是非常容易混淆
// 基类
class Person
{
protected :
string _name = "李四"; // 姓名
int _num = 333444; // 身份证号
};
// 派生类
class Student : public Person
{
public:
void Print()
{
cout << "姓名:" << _name << endl;
cout << "身份证号:" << Person::_num << endl; // 类名::成员显示访问
cout << "学号:" << _num << endl; // 打印的是派生类的学号
}
protected:
int _num = 111222; // 学号
};
int main()
{
Student s1;
s1.Print();
return 0;
};
class A
{
public:
void fun()
{
cout << "func()" << endl;
}
};
class B : public A
{
public:
void fun(int i)
{
A::fun(); // 类名::成员 还是可以访问的
cout << "func(int i)->" << i << endl;
}
};
int main()
{
B b;
b.fun(10);
return 0;
};
四、派生类的默认成员函数
// 基类
class Person
{
public :
// 默认构造函数
Person(const char* name = "Peter")
: _name(name )
{
cout << "Person()" << endl;
}
// 拷贝构造函数
Person(const Person& p)
: _name(p._name)
{
cout << "Person(const Person << endl;
}
// 赋值重载函数
Person& operator=(const Person& p )
{
cout << "Person operator=(const Person << 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 << endl ;
}
// 自己实现派生类的赋值重载函数
Student& operator = (const Student& s )
{
cout << "Student& operator= (const Student << endl;
if (this != &s)
{
Person::operator =(s); // 必须显示调用基类的赋值重载(这里会发生切片)
_num = s ._num;
}
return *this ;
}
~Student() // 先清理自己的资源
{
cout << "~Student()" << endl;
} // 结束后会自动调用父类的析构函数
protected :
int _num ; //学号
};
int main()
{
Student s1 ("Jack", 18);// 调用构造函数
Student s2 (s1); // 调用拷贝构造函数
Student s3 ("Rose", 17);
s1 = s3 ; // 调用重载赋值函数
return 0;
}
五、继承与友元
class Student;
// 基类
class Person
{
public:
friend void Display(const Person& p, const Student // 声明该函数是基类的友元
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;
Display(p, s);
return 0;
}
六、继承与静态成员
//无论继承多少层,大家用的始终是同一个变量
// 基类
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 ; // 研究科目
};
void TestPerson()
{
Student s1 ;
Student s2 ;
Student s3 ;
Graduate s4 ;
cout << " 人数 :" << Person ::_count << endl;
Student ::_count = 0;
cout << " 人数 :" << Person ::_count << endl;
}
七、复杂的菱形继承及菱形虚拟继承
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()
{
// 这样会有二义性无法明确知道访问的是哪一个
Assistant a ;
a._name = "Peter";
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
return 0;
}
class Person
{
public :
string _name ; // 姓名
};
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 ; // 主修课程
};
int main()
{
Assistant a ;
a._name = "Peter"; //这个时候_name其实只有一个
// 这下面两个显示访问其实访问的都是一个变量
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
return 0;
}
class A
{
public:
int _a;
};
// class B : public A
class B : virtual public A
{
public:
int _b;
};
// class C : public A
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
为什么 D 中 B 和 C 部分要去找属于自己的 A ?那么看看当下面的赋值发生时,d 是不是要去找出 B/C 成员中的 A 才能赋值过去?
D d;
B b;
B* p1 = // B对象指针 -> D对象,把D对象切片给B对象指针
p1->_a; // 指针访问虚基类A的成员_a
B* p2 = // B对象指针 -> B对象
p2->_a; // 指针访问虚基类A的成员_a
(1)为什么需要偏移量?
(2)为什么不在 0x005EF75C、0x005EF764 直接存偏移量?
(3)virtual 已经能解决菱形继承所带来的问题了,为什么还是不建议使用?
下面是上面的 Person 关系菱形虚拟继承的原理解释:
八、继承的总结和反思
// Car和BMW Car和Benz构成is-a的关系
class Car{
protected:
string _colour = "白色"; // 颜色
string _num = "粤DK0896"; // 车牌号
};
class BMW : public Car{
public:
void Drive()
{
cout << "好开-操控" << endl;
}
};
class Benz : public Car{
public:
void Drive()
{
cout << "好坐-舒适" << endl;
}
};
// Tire和Car构成has-a的关系
class Tire{
protected:
string _brand = "Michelin"; // 品牌
size_t _size = 17; // 尺寸
};
class Car{
protected:
string _colour = "白色"; // 颜色
string _num = "粤DK0896"; // 车牌号
Tire _t; // 轮胎
};