一文学会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;//轮胎
};

组合关系

相关推荐
用户2018792831671 分钟前
限定参数范围的注解之 "咖啡店定价" 的故事
android·java
青红光硫化黑4 分钟前
学习bug
开发语言·javascript·ecmascript
泽虞9 分钟前
C语言深度语法掌握笔记:底层机制,高级概念
java·c语言·笔记
爱coding的橙子14 分钟前
每日算法刷题Day58:8.7:leetcode 单调栈5道题,用时2h
算法·leetcode·职场和发展
董莉影35 分钟前
学习嵌入式第二十二天
数据结构·学习·算法·链表
电商数据girl36 分钟前
关于私域电商网站,接入电商API数据接口示例
运维·开发语言·网络·python·json·php
视觉CG39 分钟前
【JS】扁平树数据转为树结构
android·java·javascript
哈基米喜欢哈哈哈1 小时前
Netty入门(二)——网络传输
java·开发语言·网络·后端
老虎06271 小时前
Java基础面试题(1)—Java优势(JVM,JRE,JIT,Java类,方法)
java·开发语言·jvm
我爱学嵌入式1 小时前
C 语言第 17 天学习笔记:从二级指针到内存布局的进阶指南
c语言·笔记·学习