一文学会c++继承 组合

文章目录

继承简介

继承是面向对象程序设计代码复用的重要手段,使得程序员可以在保持原类的基础上扩展,新扩展的类叫派生类,体现类的层级结构,之前的重载是函数复用,继承是类的复用。

定义

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消失了

c 复制代码
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;

继承的作用域

c 复制代码
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,析构先调子类再基类

c 复制代码
#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;
}

继承与友元

友元不能继承,父类的朋友不是我的朋友

所以父类友元不能访问子类成员,除非子类也声明友元

c 复制代码
#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静态成员,则整个继承体系只有一个这样的成员,无论多少派生多少子类,只有这一个静态成员

c 复制代码
#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值

c 复制代码
#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;
}

指明访问解决了二义性,但数据冗余还是没解决,

虚拟继承

虚拟继承解决了二义性和数据冗余问题,

c 复制代码
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关系

  • 继承复用 基类对子类公布了成员细节,俗称白箱复用,一定程度上破坏了封装,且基类 改动对子类影响较大,子类和基类关联性强,耦合度高
  • 组合复用 被组合对象要求有良好接口,基类成员细节不可见,俗称黑箱复用,不破坏封装,关联性低,耦合度低

实战中能用组合就用组合,耦合度低,代码便于维护,不过有些情况就适合继承,多态也需要继承实现,可以用继承和组合就用组合

c 复制代码
class car
{
protected:
	string _colour;
	string _num;
};

class Benz :public car
{
public:
	void Drive()
	{
		cout << "奢华" << endl;
	}
};

继承关系

c 复制代码
class tire
{
public:
	string _brand;
	int _size;
};
class car {
	string _colour;
	string _num;//车牌
	tire _t;//轮胎
};

组合关系

相关推荐
澄澈i2 分钟前
CMake学习篇[3]---CMake进阶+嵌套CMakeLists+多层级关系
c++·学习·cmake
如意猴4 分钟前
双向链表----“双轨联动,高效运行” (第九讲)
数据结构·链表
搂鱼1145147 分钟前
GJOI 10.7/10.8 题解
算法
第二只羽毛21 分钟前
重载和继承的实践
java·开发语言
Django强哥24 分钟前
JSON Schema Draft-07 详细解析
javascript·算法·代码规范
AndrewHZ25 分钟前
【图像处理基石】GIS图像处理入门:4个核心算法与Python实现(附完整代码)
图像处理·python·算法·计算机视觉·gis·cv·地理信息系统
王嘉俊92526 分钟前
设计模式--适配器模式:优雅解决接口不兼容问题
java·设计模式·适配器模式
王嘉俊92527 分钟前
设计模式--组合模式:统一处理树形结构的优雅设计
java·设计模式·组合模式
道199335 分钟前
50 台小型无人车与50套穿戴终端 5 公里范围内通信组网方案深度研究
java·后端·struts
迎風吹頭髮40 分钟前
UNIX下C语言编程与实践35-UNIX 守护进程编写:后台执行、脱离终端、清除掩码与信号处理
java·c语言·unix