c++之继承

目录

1、继承的概念

2、使用场景

 3、继承定义

 3.1、如何继承:

3.2、继承方式

 3.3、继承父类成员访问方式的变化

4、基类和派生类对象的赋值转换

5、继承中的作用域

6、派生类的构造函数原则:

 7、多继承与菱形继承


1、继承的概念

        继承:继承就是在原有的类的基础之上进行扩展、增加一些功能。

        可以这样理解:在古代的时候,皇帝驾崩了之后,就会有一个集运气于一身的幸运之子登上皇位。这位幸运之子继承了老皇帝留下来的江山,可以在这个江山已有的基础之上实施一些新政策、新措施、最终走上富国强兵……

        看到这里应该不难理解继承是什么了吧,每一位新生的皇帝都是继承了上一任老皇帝的江山,颁布新政策。而类的继承,就是继承了已有的类,并可以在其基础之上进行扩展。

2、使用场景

        在实际的应用中,要实现一个图书管理系统,有学生、老师、各色各样的人……,难道要一一实现每个角色的类吗?那不得烦死了。这时就可以使用继承的方式。学生、老师……都具有姓名,身份证,年龄,住址等共同信息,那就可以定义一个具有共同特征的person类。然后去定义一个学生、老师等等的类就可以来继承这个person类,并且可以在其基础之上进行扩展,增加独有的信息。

//具有共同特征的人类
class Person
{
protected:
	string name;
	int age;
	string id;
	string address;
};

//学生类继承了Person类
class student:public Person
{
protected:
	//可以在原有的基础之上增加独有的信息:宿舍号
	int dormitory_id;
};

int main()
{
	Person p;
	student st;

	return 0;
}

 3、继承定义

 3.1、如何继承:

        Person是要被继承的类,叫父类,也叫基类。student继承了Person类,是子类,也叫派生类。 

        

3.2、继承方式

 3.3、继承父类成员访问方式的变化

 总结:

        1、父类的private成员,不管以什么方式被继承都是private、且不可见的:父类的私有成员被继承到了子类,子类不能访问到父类的private成员,因为语法的限制所以不能去访问,但确实有被继承下来

        2、父类的成员在基类的访问方式变化其实很好记。两个访问方式进行比较取小的那个(public > protected > private ),比如父类的public成员以public继承到子类,相等就以public访问方式。如果是以protected继承,public > protected,protected比public小所以继承到子类的访问权限就会变成protected。如果是以private继承,public > private,private比public小所以继承到子类的访问权限就会变成private。

        3、父类的protected、private成员:两个的权限都是一样的,在类外不能访问,但在类里面可以访问。

            子类继承了父类的protected、private成员:private成员不能访问,protected可以访问。

        4、一般都是使用public继承,很少会用到protected、private继承。也不提倡使用protected、private继承。

        5、继承方式如果没有写,class的默认继承方式是private,struct的默认继承方式是public。最好要显示的写出继承方式

4、基类和派生类对象的赋值转换

        子类的对象可以赋值给父类的对象、指针、引用。称为切片或切割。

        为什么叫切片呢?请看下图就不难理解了。

class Person
{
protected:
	string name;
	string _sex;
	int _age;
};

class student:public Person
{
protected:
	int _id;
};

int main()
{
	Person p;
	student st;
    
    //会把子类中的父类成员切割出来
	p = st;
	Person* ptr = &st;
	Person& rp = st;

	return 0;
}


        

 注:父类对象不能赋值给子类对象,只能子类的对象赋值给父类的对象、指针、引用

5、继承中的作用域

1、在继承体系中,子类和父类都有独立的作用域。
2.、子类和父类中如果都有同名成员的话,子类将会屏蔽对父类同名成员的直接访问,这种情况叫隐藏,也叫重定义。如果想要在子类中访问父类的同名成员,可以使用 作用域限定符 :: 来显示访问。
3. 需要注意的是:只要子类中有与父类的成员同名就会构成隐藏。
4. 切记在继承体系里面最好不要定义同名的成员。

class Person
{
protected:
	string _name="老王";
	int _age=28;
};

class student:public Person
{
public:
	void printf()
	{
		cout << "姓名:" << _name << endl;
		cout << "没有显示访问,直接访问的是子类中的年龄:" << _age << endl;	//因此这里访问到的是子类的_age成员,不会访问到父类的_age成员
		cout << "有显示访问的是父类中的年龄:" << Person::_age << endl;
	}
protected:
	int _age=33;	//子类中存在于父类同名的成员
};

int main()
{
	student st;
	st.printf();

	return 0;
}

6、派生类的构造函数原则:

子类的构造原则:先父后子

1、先调用父类的构造函数来初始化继承的父类成员。        

2、接着调用自己子类的构造函数来初始化自己的成员。

子类的析构原则:先子后父

1、先调用自己子类的析构函数来析构

2、接着再调用父类的析构函数来析构

class Person
{
public:
	Person()
	{
		cout << "Person" << endl;
	}

	~Person()
	{
		cout << "~Person" << endl;
	}
};

class Student :public Person
{
public:
	Student()
	{
		cout << "Student" << endl;
	}

	~Student()
	{
		cout << "~Student" << endl;
	}
};

int main()
{
	//子类的构造先调用父类的构造函数接着再调用自己的构造函数:先父后子
	//子类的析构先调用自己的析构函数接着再调用父类的析构函数:先子后父
	Student st;

	return 0;
}

         如想显示调用子类中的父类构造函数,请看下面的代码。

class Person
{
public:
	Person(string name="")
	{
		_name = name;
		cout << "Person" << endl;
	}

	~Person()
	{
		cout << "~Person" << endl;
	}

protected:
	string _name;
};

class Student :public Person
{
public:
	Student(string name="")
		:Person(name)	//显示调用父类的构造函数
	{
		cout << "Student" << endl;
	}

	~Student()
	{
		cout << "~Student" << endl;
	}
};

int main()
{
	Student st;

	return 0;
}

        虽然可以显示的调用父类的构造函数,但是不能显示的调用子类中的父类析构函数,这样的话会出问题:为了保证析构的顺序是先子后父,但是如果在子类的析构函数中去显示调用了父类的析构函数,就会先调用父类的析构函数,接着又析构了自己。这一系列操作完后,编译器又会自动去调用父类的析构函数,就出现多次析构同一块内存的问题了。

        因此不需要去显示调用子类中的父类析构函数,等子类析构完,编译器会自动去调用父类的析构函数。

class Person
{
public:
	Person(string name)
	{
		_name = name;
		cout << "Person" << endl;
	}

	~Person()
	{
		cout << "~Person" << endl;
	}

protected:
	string _name;
};

class Student :public Person
{
public:
	Student(string name = "")
		:Person(name)	
	{
		cout << "Student" << endl;
	}

	~Student()
	{
		//显示的去调用子类中的父类析构函数,会出现问题
		Person::~Person();

		cout << "~Student" << endl;
	}
};

int main()
{
	Student st;

	return 0;
}

 7、多继承与菱形继承

单继承:一个子类只有一个父类时称这个继承关系为单继承

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

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

         有了多继承就会有菱形继承,而菱形继承会造成数据冗余和二义性。

        1、什么是二义性呢?

              菱形继承后,类中就会有多份同样的成员,当想要去访问的时候就会出现二义性,不知道该访问的是哪个父类的成员。虽然可以显示的去调用哪个父类的成员来解决二义性。但是数据冗余依然无法解决。

class Person
{
public:
	string _name;
	int _age;
};

class student:public Person
{};

class teacher:public Person
{};

class postman :public student, public teacher
{
public:
	
};

int main()
{
	postman pm;

	//pm._name = "老王";	//子类中存在多份相同的成员,访问_name的时候不知道要访问哪个父类的成员,因此会报错
	
	//需要指定访问哪个父类的成员,方可解决二义性
	pm.student::_name="小王";
	pm.teacher::_name="大王";

	return 0;
}

        2、数据冗余?

        看到二义性是什么后,想必也已经知道数据冗余是什么了吧,就是子类中有多份相同的成员,从而造成数据冗余了。

        那如何解决数据冗余呢?在腰部的位置加虚继承就可以解决了。

class Person
{
public:
	string _name;
	int _age;
};

//腰部位置虚继承
class student: virtual public Person
{};
class teacher: virtual public Person
{};

class postman :public student, public teacher
{
public:
	
};

int main()
{
	postman pm;
	cout << sizeof(pm) << endl;

	return 0;
}

        因此不要设计出菱形继承。否则会很复杂。


 

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值