带你搞懂C++继承!!!

Posted  落禅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你搞懂C++继承!!!相关的知识,希望对你有一定的参考价值。

继承

继承:保证原有类的基础上进行扩展,是类设计层次的复用

1.class 子类名称:基础方式(public,protected,private) 父类(基类)

2.继承中private和protected的区别: 在类里面private和protected几乎没什么区别,但是在继承里面二者有一定的区别
在继承中,private继承的都不可见,但是protected方式继承的都可以看见

1.基类的private成员在派生类中无论以什么方式继承都是不可见的,这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都是不能去访问它

2.基类private成员在派生类中是不能被访问,如果基类成员不想再类外直接被访问,但需要在派生类中能访问,就定义为protected,可以看出保护成员限定夫是因为继承才出现的

3.基类的其他成员在子类的访问方式=min(成员在基类中的访问限定符,继承方式)

4.class 默认继承方式是private,struct默认继承方式位publuc

1.C++中继承方式中private和protected的区别:

如果在父类成员是protected,子类以protected/public方式继承,那么在子类中该成员为protected,就是父类中的成员在子类中可以进行访问,而在子类之外就不能进行访问

//父类中定义年龄,姓名都是保护成员,那么在子类中就可以访问该成员,
class Person

public:
	friend class Student;
protected:
	string name = "Peter";
	int age = 18;
;
class Student :protected Person

public:
	
	void Print()
	
		cout << "Name=" <<Person::name<< endl;
		cout << "Age=" << age << endl;
		cout << "id=" << id << endl;
	
	int id;
;
int main()

	Student s;
	//s.Person::Print();
	s.id = 20101320;
	s.Print();
	return 0;

如上所示,如果父类中是protected成员,那么在子类中可以访问父类的protected,而在子类外不能进行访问

如果父类的成员访问权限为private,那么子类无论以何种方式继承了,子类都没有权限访问父类中的private成员,例:

//我们将父类中的成员变量属性设置为私有,然后子类以共有的方式继承父类,看看子类可不可以访问父类
class Person

public:
private:
	string name = "Peter";
	int age = 18;
;
class Student :public Person

public:
	
	void Print()
	
		cout << "Name=" <<Person::name<< endl;
		cout << "Age=" << age << endl;
		cout << "id=" << id << endl;
	
	int id;
;
int main()

	Student s;
	//s.Person::Print();
	s.id = 20101320;
	s.Print();
	system("Pause");
	return 0;


很明显,在继承中,子类无论以何种方式继承都不可访问子类中的private内容,若要访问,可以使用友元进行访问

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员派生类的public成员派生类的protected成员派生类的private成员
基类的private成员派生类中不可见(派生类不可访问)派生类中不可见派生类中不可见

2.赋值

派生类对象可以将派生类的对象/派生类的引用/派生类的指针给父类,这里有个形象的说法叫做贴片或者切割,寓意把派生类中父类那部分切来赋值过去
父类对象不可以赋值给派生类

//将派生类的值复制给父类
//为了方便观看,我们将所有的属性设置为共有
#include<iostream>
using namespace std;
class Person

public:
	string name = "Peter";
	int age = 18;
;
class Student :public Person

public:
	int id;
;
int main()

	Student s;
	s.name = "Hello";
	s.age = 10;
	s.id = 20101320;
	Person p;
	p = s;
	return 0;

如上所示,我们手动初始化Stduent创建出来的变量s,然后将s赋值给Person类型的p,这个时候什么来看一下效果

//将派生类的应用,指针赋值给父类
#include<iostream>
using namespace std;
class Person

public:
	string name = "Peter";
	int age = 18;
;
class Student :public Person

public:
	int id=0;
;
int main()

	Student s;
	s.name = "Hello";
	s.age = 10;
	s.id = 20101320;
	Person p;
	p = s;//将派生类的值赋值给父类
	Person* prt = &s;//父类的指针可以指向派生类
	Person& inference = p;//引用

	return 0;

基类的指针可以透过强制类型转化赋值给派生类的指针,大师必须是基类的指针是指向派生类对象时才是安全的

3.继承中的作用域

1.在继承体系中基类和派生类都有独立的作用域

2.子类和父类中有同名成员,子类成员将屏蔽父亲对同名成员的直接访问,这种情况叫做影藏,也叫做重定义(在子类成员函数中,可以使用基类::成员函数
来显示访问)

3.需要注意的是如果是成员函数的隐藏,只需要函数名相同就可以构成隐藏

4.在实际继承体系中最好不要定义同名的成员

例如:

class Person

public:
	int _age = 15;
;
class Student :public Person

public:
	int _age = 25;
;

在父类和子类中定义两个名称相同的变量,默认访问的是子类的_age,如果想要访问父类的__age,就要加上作用域


如果有重名的函数也是相同的道理:

#include<iostream>
using namespace std;
class Person

public:
	void fun()
	
		cout << "Person::fun" << endl;
	
;
class Student :public Person

public:
	void fun()
	
		cout << "Student::fun" << endl;
	
;
int main()

	Student s;
	s.fun();
	s.Person::fun();

	return 0;

4.派生类的默认构造函数

6个默认构造函数,默认的意思是我们不写,编译器会为我们默认生成一个,在派生类中,这几个成员函数是如何完成的呢

1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员,如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用

2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化

3.派生类的operator=必须要调用基类的operator=完成基类的复制

4.派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员,因为这样才能保证派生类对象先清理派生类成员在清理基类成员的顺序

5.派生类对象初始化调用基类构造再调用派生类构造

6.派生类对象析构,清理先调用派生类析构再调用基类的析构

例:

#include<iostream>
using namespace std;
class Person

public:
	//构造函数
	Person(string _name="",int _age=0):
		name(_name),
		age(_age)
	
		cout << "Person构造函数" << endl;
	

	//拷贝构造函数
	Person(const Person& s):
		name(s.name),
		age(s.age)
	
		cout << "Person拷贝构造函数" << endl;
	

	//赋值构造函数
	Person& operator=(const Person& s)
	
		if (this != &s)
		
			name = s.name;
			age = s.age;
		
		cout << "Person=重载" << endl;
		return *this;
	

	//析构函数
	~Person()
	
		cout << "~Person析构函数" << endl;
	

protected:
	string name;
	int age;
;
class Student :public Person

public:
	//构造函数
	Student(string name="",int age=0,int id=0):
		Person(name,age),
		_id(id)
	
		cout << "Student构造函数" << endl;
	
	
	//拷贝构造函数
	Student(const Student& p):
		Person(p),
		_id(p._id)
	
		cout << "Student拷贝构造函数" << endl;
	

	//=重载
	Student& operator=(const Student& s)
	
		if (this != &s)
		
			Person::operator=(s);
			_id = s._id;
		
		cout << "Student=重载" << endl;
		return *this;
	
	//析构函数
	~Student()
	
		cout << "~Student析构函数" << endl;
	
private:
	int _id;
;
int main()

	Student s("校长", 26, 20101320);
	Student t(s);
	Student u;
	u = s;
	return 0;

5.继承与友元 友元关系不可以被继承 基类友元不能访问子类私有和保护成员

#include<iostream>
using namespace std;
class Student;
class Person

	friend void Print(Person& p, Student& s);
public:
protected:
	int num=10;
;
class Student :public Person

public:
protected:
	int _num = 20;
;
void Print(Person& p, Student& s)

	cout <<p.num << endl;
	cout << s._num << endl;

int main()

	Person p;
	Student s;
	Print(p, s);

return 0;


上述代码我们在父类中定义void Print(Person& p, Student&
s);该函数为友元函数,在子类中进行继承,发现该函数只能访问父类中的成员,而子类中的私有成员访问不了,因此友元不可以进行传递,若要使该程序正常运行,就需要在子类中也定义友元函数,例如,把上面的代码改成下面这样

6.继承与静态成员变量

继承与静态成员 静态成员被继承后所有成员共享,同时占有一个空间

#include<iostream>
using namespace std;
class Person

public:
	Person()
	
		count++;
	
	static int count;//静态成员变量在类内进行声明,类外进行初始化

;
int Person::count = 0;
class Student :public Person

public:

;
int main()

	Student s1;
	Student s2;
	Student s3;
	Student s4;
	cout << Person::count << endl;

	return 0;

7.继承关系

单继承:

一个子类只能由一个直接父类时称这个继承关系为单继承

多继承:

一个子类有两个或以上直接父类时称这个继承关系为多继承

菱形继承:

菱形继承是多继承的一种特殊情况

菱形继承问题:

从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性问题

//如下所示,我们写了一个菱形继承,Student继承了Person,Teacher也继承了Person,然后Assistant继承了Student和Person
#include<iostream>
using namespace std;
class Person

public:
	string _name;
;
class Student :public Person

protected:
	int  _num;
;
class Teacher :public Person

protected:
	int _id;
;
class Assitstant :public Student, public Teacher

protected:
	string _majorCourse;
;
int main()

	Assitstant a;
	a._name = "hello";
	return 0;

上述代码中Assistant继承了Student和Teacher,而这两个类又分别继承了Person,这就导致了Assistant拥有了两个Person的数据段,出现了二义性

可以通过指明作用域改变上述问题,但是这又会产生数据冗余

虚拟继承可以解决菱形继承的二义性和数据冗余的问题,如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题,需要注意的是,虚拟继承不能在其它地方去使用

#include<iostream>
using namespace std;
class Person

public:
	string _name;
;
class Student :virtual public Person

protected:
	int  _num;
;
class Teacher :virtual public Person

protected:
	int _id;
;
class Assitstant :public Student, public Teacher

protected:
	string _majorCourse;
;
int main()

	Assitstant a;
	a._name = "Peter";

	return 0;

决菱形继承的二义性和数据冗余的问题,如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题,需要注意的是,虚拟继承不能在其它地方去使用

#include<iostream>
using namespace std;
class Person

public:
	string _name;
;
class Student :virtual public Person

protected:
	int  _num;
;
class Teacher :virtual public Person

protected:
	int _id;
;
class Assitstant :public Student, public Teacher

protected:
	string _majorCourse;
;
int main()

	Assitstant a;
	a._name = "Peter";

	return 0;

以上是关于带你搞懂C++继承!!!的主要内容,如果未能解决你的问题,请参考以下文章

C++继承不会怎么办?一篇文章带你搞懂C++继承!!!

一篇文章带你搞懂Python中的继承和多态

一篇文章带你搞懂Python中的继承和多态

一文带你搞懂C++如何操作文件对话框(附源码)

一篇文章带你搞懂 Java 注解的原理

带你搞懂朴素贝叶斯分类算法