一文学会c++继承 组合
文章目录
- 继承简介
- 定义
- 访问限定符和继承方式
- ⭐基类派生类赋值转换
- 继承的作用域
- 派生类的默认成员函数
- 继承与友元
- 继承与静态成员
- ⭐复杂的菱形继承
- 虚拟继承
- 组合
继承简介
继承是面向对象程序设计代码复用的重要手段,使得程序员可以在保持原类的基础上扩展,新扩展的类叫派生类,体现类的层级结构,之前的重载是函数复用,继承是类的复用。
定义
#include <iostream>
#include <string>
using namespace std;
class person
{
public:void print(){cout << _name << " " << _sex << " " << _age << endl;;}
protected:string _name ="Jack";string _sex ="man";int _age=18;
};
class student : public person
{
protected:int _number;
};
int main()
{person p1;student s1;p1.print();s1.print();return 0;
}
可以看到,student是派生类,public是继承方式,person是基类
派生类拥有基类的所有成员
访问限定符和继承方式
总结:
1,基类的private成员,无论什么继承,派生类都不可见,但是成员还是继承到了派生类中,但是语法限制了派生类访问
2,基类的private在派生类不可见,要想突破限制,就用protected,在类外不可访问,但在派生类可访问,访问限制符protected是为继承准备的
3,观察上面表格,可以发现两个不同限定符,取范围更小的,如protected和private 取private
4,class类默认继承方式private,struct类默认public,一般都写出继承方式的
5,一般都是public继承,几乎很少private/protected,
⭐基类派生类赋值转换
🚩 派生类对象可以赋值基类对象/基类引用/基类指针,这个过程叫切片,意为着把派生类那块基类所有的切给基类,
🚩基类对象不能赋值给派生类
如图,派生可以赋值给基类,只不过 _number消失了
int main()student s2;person p2 = s2;person* pp2 = &s2;person& ppp2 = s2;student* ss2 = (student*)pp2;//这种强转可以pp2 = &p2;//student* ss2 = (student*)pp2;//这种会越界访问return 0;
继承的作用域
class person
{
public:void print(){cout << "基类" << endl;}
};
class student : public person
{
public:void print(){cout << "派生类" << endl;}
};
int main()
{student s3;s3.print();s3.student::print();s3.person::print();return 0;
}
1,基类和派生类都有独立的作用域
🚩2,基类与派生类成员重名时,子类调用会直接跳过父类成员,这叫隐藏,也叫重定义,或者可用 类名::成员 访问
🚩3,在一个作用域的重名叫重载,两个作用域重名叫隐藏
4,继承体系最好不要定义重名成员
派生类的默认成员函数
1,派生类对象初始化先调用基类构造函数,若基类无默认构造函数,则要在初始化列表显式调用,再调用自己的构造函数
2,构造,赋值,拷贝构造都是先调基类再调子类
3,析构先调子类再基类
#include <iostream>
#include <string>
using namespace std;
class person
{
public:person(string name ="jack"):_name(name){cout << "person()" << endl;}person(const person& p1):_name(p1._name){cout << "person(& p1)" << endl;}person& operator=(const person& p1){cout << "operator(const person&p1)" << endl;if(this!=&p1){_name = p1._name;}return *this;}~person(){ }
protected:string _name;
};
class student : public person
{
public:student(const string name="peter", int number = 110):person(name),_number(number){cout << "student()" << endl;}student(const student& s):person(s),_number(s._number){cout << "student(student& s)" << endl;}student& operator=(const student& s){if (this != &s){person::operator= (s);_number = s._number;}cout << "operator=(const student &s)" << endl;return *this;}~student(){ }
protected:int _number;
};
int main()
{person p1("peter");student s1("jack", 11);student s2;s2 = s1;return 0;
}
继承与友元
友元不能继承,父类的朋友不是我的朋友
所以父类友元不能访问子类成员,除非子类也声明友元
#include <iostream>
#include <string>
using namespace std;
class student;//声明
class person {
public:friend void Display(const person& p, const student& s);
protected:string _name = "person";
};class student : public person {
public:friend void Display(const person& p, const student& s); // 子类也声明为友元student(int num = 0) : _number(num) {}
protected:int _number;
};void Display(const person& p, const student& s) {cout << p._name << endl;cout << s._number << endl; // 现在可以访问(student 的友元)
}int main() {person p;student s(12345);Display(p, s);return 0;
}
继承与静态成员
🚩基类定义static静态成员,则整个继承体系只有一个这样的成员,无论多少派生多少子类,只有这一个静态成员
#include <iostream>
#include <string>
using namespace std;
class person
{
public:person() {++_count;}string _name;static int _count;
};
int person ::_count = 0;
class student:public person
{
public:student():person(){++_count;}
};
int main()
{student s;cout << person::_count<<endl;return 0;
}
2
⭐复杂的菱形继承
单继承:一个子类只有一个父类
多继承:一个子类有两个及以上的父类
菱形继承:多继承的一种
下图,可看出菱形继承数据冗余和二义性问题,有两份person值
#include <iostream>
#include <string>
using namespace std;
class person
{
public:string _name;
};
class student :public person
{
public:string _num;
};
class teacher :public person
{
public:string _id;
};
class assistant :public student, public teacher
{
public:string _majorcourse;
};
int main()
{assistant a;//a._name = "peter";//❌不明确a.student::_name = "peter";//可以指明访问,a.teacher::_name = "jack";return 0;
}
指明访问解决了二义性,但数据冗余还是没解决,
虚拟继承
虚拟继承解决了二义性和数据冗余问题,
class person
{
public:string _name;
};
class student :virtual public person
{
public:string _num;
};
class teacher :virtual public person
{
public:string _id;
};
class assistant :virtual public student, virtual public teacher
{
public:string _majorcourse;
};
原理:
只存一个person,student和teacher中的person 存到assistant的地址偏移量,用时再跳过去找
注意:
菱形虚拟继承底层复杂,一般不设计多继承,更不能设计菱形继承
组合
继承是 is-a关系
组合是 has-a关系
- 继承复用 基类对子类公布了成员细节,俗称白箱复用,一定程度上破坏了封装,且基类 改动对子类影响较大,子类和基类关联性强,耦合度高
- 组合复用 被组合对象要求有良好接口,基类成员细节不可见,俗称黑箱复用,不破坏封装,关联性低,耦合度低
实战中能用组合就用组合,耦合度低,代码便于维护,不过有些情况就适合继承,多态也需要继承实现,可以用继承和组合就用组合
class car
{
protected:string _colour;string _num;
};class Benz :public car
{
public:void Drive(){cout << "奢华" << endl;}
};
继承关系
class tire
{
public:string _brand;int _size;
};
class car {string _colour;string _num;//车牌tire _t;//轮胎
};
组合关系