[C++]:15.继承

继承

  • 一.继承:
    • 1.继承的概念和基本操作:
      • 1.概念:
      • 2.基本操作:
    • 2.继承格式和多种继承方法:
      • 1.基本继承格式:
      • 2.继承关系+访问限定符
    • 3.子类对象和父类对象之间的赋值:
      • 1.为什么存在赋值兼容转换?
      • 2.子类对象赋值给父类对象(赋值兼容转换):
        • 2-1:对象赋值:
        • 2-2:对象指针赋值:
        • 2-3:对象引用赋值:
      • 3.总结:
    • 4.继承中的作用域:
      • 1.问题:子类和父类可以有同名成员?
        • 1-1:同名成员:
        • 1-2:同名函数:
      • 2.问题:子类和父类成员函数不是重载关系?
      • 3.总结:
    • 5.子类的默认成员函数!
      • 1.构造函数:
      • 2.析构函数:
      • 3.拷贝:
      • 4.赋值:
    • 7.继承和友元+继承和静态:
      • 1.继承和友元:
      • 2.继承和静态:
    • 8.多继承:
      • 1.菱形继承:特殊的多继承
      • 2.1.菱形虚拟继承:
    • 9.继承的总结和反思:
      • 1.多继承
      • 2组合和继承:
      • 3.继承的作用:

一.继承:

1.继承的概念和基本操作:

1.概念:

继承就是一种类的复用,我们以前写的代码比较多的都是在主函数中使用各种函数这是函数的复用,在接触类和对象以后需要学习类的复用也就是继承。继承是面向对象的,这样的语法可以让程序员在原来的类的基础上继承产生一个新的的类,这个新的的类有被继承类的成员函数和成员变量。

2.基本操作:

两个类一个老师类,一个学生类,提取两个类中共有的内容去反向定义一个类person类让老师类和学生类都去继承人这个类实现类的复用:我们先去写一个关于学生的复用。

	class preson {
	public:
		preson(string name="xxxx", string six="xxx", int age=18)
			:_name(name)
			,_six(six)
			,_age(age)
		{}
		void information_person()
		{
			cout << "名字:" << _name << endl;
			cout << "性别:" << _six << endl;
			cout << "年龄:" << _age << endl;
		}
	protected:
		string _name;
		string _six;
		int _age;
	};

	class student : public preson {
	public:
		student(int score,int grade)
			:_score(score)
			,_grade(grade)
		{}
		void information_student()
		{
			//1.被继承的成员变量可以直接使用:
			cout << "名字:" << _name << endl;
			cout << "性别:" << _six << endl;
			cout << "年龄:" << _age << endl;
			//2.间接使用:
			information_person();
			//3.学生部分:
			cout << "成绩:" << _score << endl;
			cout << "年级:" << _grade << endl;
		}
	protected:
		int _score;
		int _grade;
	};

	int main()
	{
		sfpy::student s1(88, 1);
		s1.information_student();
		return 0;
	}

[C++]:15.继承_第1张图片

观察代码和运行结果,我们发现被继承的这个类当前的成员变量和成员函数我们都可以去使用,这个地方没有办法去指定学生的person类中的成员内容?

2.继承格式和多种继承方法:

1.基本继承格式:

[C++]:15.继承_第2张图片

2.继承关系+访问限定符

1.继承方式:public继承 ,protected继承 , private继承。
2.访问方式:public访问 ,protected访问 , private访问。
4.继承方式+访问方式才可以确定子类对父类的成员的使用情况!
5.注意:
5-1:不去显示的去写继承方式,class来说默认private继承 , struct是公有继承。
5-2:建议:public继承方式 + 成员变量使用保护 + 成员方法使用公有。
*
总结:父类的 A 成员 和 子类的B继承组合在一起,对于子类来说对于子类来说这个父类的成员在子类中有着 A&&B中较小的一个情况。
*
public > protected > private;
保护就是因为继承才产生的,公有+保护 保护+公有 保护+保护 对于子类来说都可以去使用父类的成员!
[C++]:15.继承_第3张图片

3.子类对象和父类对象之间的赋值:

1.为什么存在赋值兼容转换?

不同类型之间在进行 赋值,大小比较,的时候都会产生临时变量。通过临时变量进行操作。下面这个地方通过a的值产生了一个float类型的临时变量然后临时变量和b进行比较在这个地方。

int main()
{
	int a = 10;
	float b = 20.5;
	
	if (a > b)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a << endl;
	}
	return 0;
}

子类赋值个父类的过程中是不存在中间变量的?
回答:临时变量具有常性不能把具有const类型的数据赋值给person对象。
解决办法:特殊处理—>赋值兼容转换—>中间不产生临时变量,做了一个切片处理。

2.子类对象赋值给父类对象(赋值兼容转换):

子类的对象---->赋值给---->父类对象
子类的对象指针---->赋值给---->父类对象指针
子类的对象引用---->赋值给---->父类对象引用

int main()
{
	sfpy::student s1(88, 1);
	//1.切片赋值--->产生新的切片:
	sfpy::preson p1 = s1;
	//2.切片赋值
	sfpy::preson* p2 = &s1;
	//3.切片赋值
	sfpy::preson& p3 = s1;
	return 0;
}
2-1:对象赋值:

[C++]:15.继承_第4张图片

2-2:对象指针赋值:

[C++]:15.继承_第5张图片

2-3:对象引用赋值:

[C++]:15.继承_第6张图片

3.总结:

[C++]:15.继承_第7张图片

4.继承中的作用域:

1.问题:子类和父类可以有同名成员?

回答:子类和父类属于不同的作用域可以有同名成员!

1-1:同名成员:
class preson {
	public:
		preson(string name = "xxxx", string six = "xxx", int age = 18)
			:_name(name)
			, _six(six)
			, _age(age)
		{}
		void information()
		{
			cout << "名字:" << _name << endl;
			cout << "性别:" << _six << endl;
			cout << "年龄:" << _age << endl;
		}
	protected:
		string _name;
		string _six;
		int _age;
	};

	class student : public preson {
	public:
		student(string name ,int score, int grade)
			:_name(name)
			,_score(score)
			, _grade(grade)
		{}
		void information()
		{
			//1.被继承的成员变量可以直接使用:指定类域
			cout << "名字:" << preson::_name << endl;
			cout << "性别:" << _six << endl;
			cout << "年龄:" << _age << endl;


			cout << "名字:" << _name << endl;
			cout << "成绩:" << _score << endl;
			cout << "年级:" << _grade << endl;
		}
	protected:
		string _name;
		int _score;
		int _grade;
	};

[C++]:15.继承_第8张图片

1.成员变量的一个同名成员,子类优先访问自己的成员变量,如果在这样的情况下想要去访问父类的成员变量需要指定类域去进行访问。
2.父子类的同名成员变量,构成隐藏关系。

1-2:同名函数:
namespace sfpy {
	class preson {
	public:
		preson(string name="xxxx", string six="xxx", int age=18)
			:_name(name)
			,_six(six)
			,_age(age)
		{}
		void information()
		{
			cout << "名字:" << _name << endl;
			cout << "性别:" << _six << endl;
			cout << "年龄:" << _age << endl;
		}
	protected:
		string _name;
		string _six;
		int _age;
	};

	class student : public preson {
	public:
		student(int score,int grade)
			:_score(score)
			,_grade(grade)
		{}
		void information()
		{
			//1.被继承的成员变量可以直接使用:
			cout << "名字:" << _name << endl;
			cout << "性别:" << _six << endl;
			cout << "年龄:" << _age << endl;

			2.间接使用:指定类域
			person::information();
			
			//3.学生部分:
			cout << "成绩:" << _score << endl;
			cout << "年级:" << _grade << endl;
		}
	protected:
		int _score;
		int _grade;
	};

}

1.成员变量的一个同名成员,子类优先访问自己的成员变量,如果在这样的情况下想要去访问父类的成员函数需要指定类域去进行访问。
2.父子类的同名成员函数,构成隐藏关系,不需要考虑返回值和参数的情况!

2.问题:子类和父类成员函数不是重载关系?

回答:构成重载需要成员函数在同一作用域下!

3.总结:

1.在实际的情况当中不需要去定义同名的成员!
2.注意函数重载和隐藏关系的不同。

5.子类的默认成员函数!

子类和父类的这些默认成员函数进行分别调用的!
1.对子类做处理就去掉用子类
2.对父类做处理就去掉用父类

1.构造函数:

1.不去使用父类的默认构造通过显示的去调用父类的构造可以通过子类传参的方法对父类的成员进行初始化构造。
2.父类有默认构造函数那么如果我们不去显示的调用构造那么走默认构造(无参,全缺省,列表初始化)

	class preson {
	public:
		preson(string name = "xxxx", string six = "xxx", int age = 18)
			:_name(name)
			, _six(six)
			, _age(age)
		{}
		void information_person()
		{
			cout << "名字:" << _name << endl;
			cout << "性别:" << _six << endl;
			cout << "年龄:" << _age << endl;
		}
	protected:
		string _name;
		string _six;
		int _age;
	};

	class student : public preson {
	public:
		student(string name,string six, int age,int score, int grade)
			:preson(name,six,age)
			, _score(score)
			, _grade(grade)
		{}
		void information_student()
		{
			//1.被继承的成员函数可以直接使用:
			information_person();
			cout << "成绩:" << _score << endl;
			cout << "年级:" << _grade << endl;
			cout << endl;
		}
	protected:
		int _score;
		int _grade;
	};

[C++]:15.继承_第9张图片

2.析构函数:

[C++]:15.继承_第10张图片

为什么我们在子类的析构函数中显示的去掉用父类的析构函数会报错?
1.有父子关系的两个类的析构函数名称会被统一转化为distruct。
2.两个类的析构函数就构成了隐藏的关系。
3.同名称的函数需要指定类域。

[C++]:15.继承_第11张图片

我们如果去显示的去析构对象,先析构子的部分还是父的部分?
1.我们应该先析构子节点再去析构父节点。
2.因为如果存在先析构父节点那么如果子节点的析构操作需要父节点的数值怎么办?
3.为了保证先去调用子节点后调用父节点子类的析构函数不需要显示的去调用父类的析构函数。编译器会在调用完子类的析构然后自动的去调用父类的析构

3.拷贝:

1.编译器默认生成的 拷贝构造会去完成值拷贝。
2.对于当前的例子来说完成值拷贝是没有问题的。
3.显示的去调用父类的拷贝构造子类的初始化列表中去使用。
4.我们知道在一些情况下有可能发生浅拷贝的问题是需要进行显示调用。

class preson {
public:
	preson(string name = "xxxx", string six = "xxx", int age = 18)
		:_name(name)
		, _six(six)
		, _age(age)
	{}
	preson(const preson& p1)
	{
		_name = p1._name;
		_six = p1._six;
		_age = p1._age;
	}
	void information_person()
	{
		cout << "名字:" << _name << endl;
		cout << "性别:" << _six << endl;
		cout << "年龄:" << _age << endl;
	}
	~preson()
	{
		cout << "~preson" << endl;
	}
protected:
	string _name;
	string _six;
	int _age;
};

class student : public preson {
public:
	student(string name, string six, int age, int score, int grade)
		:preson(name, six, age)
		, _score(score)
		, _grade(grade)
	{}
	student(const student& s1)
		:preson(s1)
		,_score(s1._score)
		,_grade(s1._grade)
	{}
	void information_student()
	{
		//1.被继承的成员函数可以直接使用:
		information_person();
		cout << "成绩:" << _score << endl;
		cout << "年级:" << _grade << endl;
		cout << endl;
	}
	~student()
	{
		preson::~preson();
		cout << "~student" << endl;
	}
protected:
	int _score;
	int _grade;
};

}

4.赋值:

class preson {
public:
	preson(string name = "xxxx", string six = "xxx", int age = 18)
		:_name(name)
		, _six(six)
		, _age(age)
	{}
	preson(const preson& p1)
	{
		_name = p1._name;
		_six = p1._six;
		_age = p1._age;
	}
	void information_person()
	{
		cout << "名字:" << _name << endl;
		cout << "性别:" << _six << endl;
		cout << "年龄:" << _age << endl;
	}
	preson& operator=(const preson& p)
	{
		//1.不能自己给自己进行赋值:
		if (this != &p)
		{
			_name = p._name;
			_six = p._six;
			_age = p._age;
		}
		return *this;

	}
	~preson()
	{
		cout << "~preson" << endl;
	}
protected:
	string _name;
	string _six;
	int _age;
};

class student : public preson {
public:
	student(string name, string six, int age, int score, int grade)
		:preson(name, six, age)
		, _score(score)
		, _grade(grade)
	{}
	student(const student& s1)
		:preson(s1)
		,_score(s1._score)
		,_grade(s1._grade)
	{}
	void information_student()
	{
		//1.被继承的成员函数可以直接使用:
		information_person();
		cout << "成绩:" << _score << endl;
		cout << "年级:" << _grade << endl;
		cout << endl;
	}
	student& operator=(const student& s)
	{
		//1.不能自己给自己进行赋值:
		if (this != &s)
		{
			//2.分开显示的调用:
			preson::operator=(s);
			_score = s._score;
			_grade = s._grade;
		}
		return *this;
	}
	~student()
	{
		preson::~preson();
		cout << "~student" << endl;
	}
protected:
	int _score;
	int _grade;
};

}

[C++]:15.继承_第12张图片

7.继承和友元+继承和静态:

1.继承和友元:

1.友元关系不可以继承,子类的友元不能访问子类的私有和保护。
2.如果想要使用友元函数那么应该在自己的类域中去声明友元函数。

2.继承和静态:

1,父类定义了一个static成员那么无论有多少子类继承,产生了多少对象,都只有这一个static成员实例。

8.多继承:

1.单继承:一个子类只去直接继承一个父类。

class A {
public:
	A(int a)
	:_a(a)
	{
		cout << "A()" << endl;
	}
protected:
	int _a;
};

class B : public A{
public:
	B(int a, int b)
		:A(a)
		,_b(b)
	{
		cout << "B()" << endl;
	}
protected:
	int _b;
};

class C : public B {
public:
	C(int a, int b,int c)
		:B(a,b)
		, _c(c)
	{
		cout << "B()" << endl;
	}
protected:
	int _c;
};

2.多继承:一个子类直接继承多个父类。

class A {
public:
	A(int a)
	:_a(a)
	{
		cout << "A()" << endl;
	}
protected:
	int _a;
};

class B{
public:
	B(int b)
		:_b(b)
	{
		cout << "B()" << endl;
	}
protected:
	int _b;
};

class C : public B,public A {
public:
	C(int a, int b,int c)
		:B(b)
		,A(a)
		, _c(c)
	{
		cout << "B()" << endl;
	}
protected:
	int _c;
};

1.菱形继承:特殊的多继承

[C++]:15.继承_第13张图片

菱形继承存在两个问题:
1.数据冗余:存储了很多不需要的数据。
2.二意性:作为老师有一个名称,作为学生有一个名称。

class preson {
public:
	preson(string name)
		:_name(name)
	{}

protected:
	string _name;
};

class student : public preson{
public:
	student(string name , int id)
		:preson(name)
		,_student_id(id)
	{}
protected:
	int _student_id;
};

class Teacher : public preson {
public:
	Teacher(string name, int id)
		:preson(name)
		, _teacher_id(id)
	{}
protected:
	int _teacher_id;
};

class Assistan : public student,public Teacher {
public:
	Assistan(string name_1 , string name_2,int id_1 , int id_2 , int money)
		:student(name_1,id_1)
		,Teacher(name_2,id_2)
		,_money(money)
	{}
protected:
	int _money;
};

1.person有一个属性名称。
2.作为学生有学生编号和名称。
3.作为老师有老师编号和名称。
4.作为助理继承了老师和学生的两个身份同时可以有工资这个属性。
5.这么一看好像没有什么问题但是人的公有属性名称在内存中的存贮是什么样子的?
6.如果说presson类有非常多的数据名字,住址,性别,身份证号等等,我们需要存贮两份数据吗?

[C++]:15.继承_第14张图片
[C++]:15.继承_第15张图片

2.1.菱形虚拟继承:

class A {
public:
	A(int a = 0)
	:_a(a)
	{
		cout << "A()" << endl;
	}
protected:
	int _a;
};

class B : virtual public A{
public:
	B(int a , int b)
		:A(a)
		,_b(b)
	{
		cout << "B()" << endl;
	}
protected:
	int _b;
};

class C : virtual public A{
public:
	C(int a, int c)
		:A(a)
		,_c(c)
	{
		cout << "B()" << endl;
	}
protected:
	int _c;
};

class D :  public B , public C{
public:
	D(int a,int b,int c,int d)
		:B(a,b)
		,C(a,c)
		,_d(d)
	{}
protected:
	int _d;
};

为什么有虚继承?
在语法上新增加了一个虚继承去解决数据冗余和二意性。
注意:虚拟继承可以解决菱形继承继数据冗余和二意性问题,只能在B和C中去使用虚拟继承就可以解决问题。从底层来看相同的数据只会去保存一份节省了空间。

[C++]:15.继承_第16张图片
[C++]:15.继承_第17张图片

[C++]:15.继承_第18张图片

9.继承的总结和反思:

1.多继承

1.多继承是C++的缺陷,有了多继承就有菱形继承,不得不新增语法解决问题。

2组合和继承:

1.我们应该优先去使用组合而不是继承。
黑箱:组合
1.我们使用组合对象之间关系不太一个类被维护不会影响到另一个类。
2.为什么称为黑箱呢?
3.使用一个对象我们只需要去使用对象的接口我们不需要关心对象中内容只需要去使用相关的接口。对象里面的内容我们是看不见的!
4.组合之间没有非常强的依赖关系,耦合度底。

白箱:继承
1.对于子类来说父类就是一个白箱,父类中的内容我们大部分都可以访问到。操>作父类内容比较麻烦可能产生问题,
2.改变父类会影响子类,子类和父类的依赖关系是非常强的。
3.代码的耦合度是非常高的。

3.继承的作用:

在实际情况中可以使用组合就使用组合,组合的耦合度底,代码方便维护,对于继承来说合适使用继承的地方还是可以去使用继承,多态的前提条件也是需要有继承关系的。

你可能感兴趣的:(c++,开发语言,继承)