C++学习之路-多继承

定义

多继承,顾名思义就是继承多个父类。以往派生类只有一个基类,叫作单继承。派生类有两个或者两个以上的基类,就叫作多继承。
多继承的语法也很简单,将多个基类用逗号隔开。
例如已声明了类A、类B和类C,那么可以这样来声明派生类D:

class D: public A, private B, protected C
{
    //类D新增加的成员
}

可以看到一个细节,可以分别用不同的方式继承基类,比如public, private, protected。但我们学过继承(可以看之前的博文),继承方式尽量只选public。

派生类初始化多个基类中的成员变量

派生类初始化基类中的private成员变量,都需要借助初始化列表的初始化方式,并且调用基类中的构造函数,间接的去初始化基类中的成员变量。

class Person
{
	int m_age;
public:
	Person(int age): m_age(age){
		cout << "Person::Person age = " << m_age << endl;
	}
};

class Student
{
	int m_score;
public:
	Student(int score):m_score(score){
		cout << "Student::Student m_score = " << m_score << endl;
	}
};

class Undergraduate : public Person , public Student
{
	int m_grade;
public:
	// 由于在工程中 m_age和m_score大多数是私有的,所以不可以直接对其进行赋值,下方代码报错
	Undergraduate(int age, int score, int grade): m_age(age),m_score(score),m_grade(grade){} 

	// 我们知道,子类构造函数可以调用父类的构造函数,且必须通过初始化列表
	// 所以,子类初始化父类中的成员变量,用子类构造函数调用父类构造函数的方法可以实现
	Undergraduate(int age, int score, int grade) : Person(age), Student(score), m_grade(grade){
		cout << "Undergraduate::Undergraduate m_grade = " << m_grade << endl;
	}

};

所以我们在主函数中,实例Undergraduate对象时,(将对象放在栈空间或者堆空间)就要这样实例化:传三个参数,调用Undergraduate类中的有参构造函数,分别使得age=20;score=100;grade=4

int main()
{
	
	Undergraduate ug(20,100,4);
	Undergraduate *p_ptr = new Undergraduate(20, 100, 4);
	
	// 这个的字节存储顺序是,先继承谁,谁的变量就放在前面
	// 	class Undergraduate : public Person , public Student 先继承的Person类,所以m_age就放在了这块内存的前面

	getchar();
	return 0;
}

对象所在的内存空间,都有什么,怎么排列?

ug对象所在的内存里分别有m_age、m_score、m_grade,总共占12个字节。

这个的字节存储顺序是,先继承谁,谁的变量就放在前面。

由于是(class Undergraduate : public Person , public Student )先继承的Person类,所以m_age就放在了这块内存的前面

多继承时,派生类与基类同名函数问题

假设基类和派生类中存在同名的函数(不包括重载函数)。那派生类对象调用函数时,会出现什么情况呢?

class Person
{
	int m_age;
public:
	// 同名函数
	void eat()
	{
		cout << "Person::eat()" << endl;
	}

};

class Student
{
	int m_score;
public:
	// 同名函数
	void eat()
	{
		cout << "Student::eat()" << endl;
	}

};

class Undergraduate : public Person , public Student
{
	int m_grade;
public:
	//同名函数
	void eat()
	{
		cout << "Undergraduate::eat()" << endl;
	}
};

我们知道,继承会把基类中的函数一并继承下来。到时候派生类Undergraduate 里有三个eat函数,Ug.eat();会调用哪个函数呢?

结果是调用 Undergraduate类下的eat函数, 这无可厚非

但我想要调用父类中的eat怎么办呢? 看代码:

int main()
{
	// 同名函数问题。
	Undergraduate Ug;
	Ug.eat(); //调用同名函数,此时调用 Undergraduate类下的eat 这无可厚非
	// 但我想要调用父类中的eat怎么办呢? 下方代码可以解决
	Ug.Person::eat();
	Ug.Student::eat();

	getchar();
	return 0;
}

指定作用域,就可以指定去调用哪个类的函数,这是多继承时需要掌握的知识点

Ug.Person::eat(); 
Ug.Student::eat();

多继承时,派生类与基类同名成员变量问题

基类和派生类存在同名的成员变量,那派生类对象调用变量时,会出现什么情况呢?

class Person
{
	int m_age;
public:
	int m_same;
};

class Student
{
	int m_score;
public:
	int m_same;
};

class Undergraduate : public Person , public Student
{
	int m_grade;
public:
	int m_same;
};

不说废话了,调用规则和同名成员函数一样,需要指定作用域。

int main()
{
	// 同名成员变量问题
	Undergraduate UG;
	UG.m_same = 10;	//访问同名成员变量,此时访问Undergraduate类下m_same,这无可厚非
	// 但我想要访问父类中的m_same怎么办呢? 下方代码可以解决
	UG.Person::m_same = 20;
	UG.Student::m_same = 30;
	
	getchar();
	return 0;
}

在汇编眼里,这三个同名变量,是不同名的。

子类想要创建无参构造函数,父类如果有构造函数,最起码得有无参构造函数

这里温习一个知识点,构造函数那块的知识点。子类想要创建无参构造函数,父类如果有构造函数,最起码得有无参构造函数。为什么突然温习这个知识点呢,因为我写代码时,在这里出错了,一时间找不到问题所在,然后翻了翻构造函数的文章,才想起来。

例子是这样的:有一个基类Student,和一个Undergraduate 类派生于Student。派生类想要初始化父类中的成员变量m_score,就必须通过调用基类中有参的构造函数初始化,到这里没问题。派生类存在有参构造函数,基类中也存在有参构造函数。但我此时有一个应用场景,希望创建对象时,调用Undergraduate 的无参构造函数,因此我在Undergraduate 类中声明了这个无参构造函数

class Student
{
	int m_score;
public:
	Student(int score):m_score(score){
		cout << "Student::Student m_score = " << m_score << endl;
	}

};

class Undergraduate :  public Student
{
	int m_grade;
public:
	int m_same;
	Undergraduate(){}//无参构造函数:子类想要创建无参构造函数,父类如果有构造函数,最起码得有无参构造函数
	Undergraduate(int age, int grade) :  Student(score), m_grade(grade){
		cout << "Undergraduate::Undergraduate m_grade = " << m_grade << endl;
	}
};

结果就报错了:类Student中没有默认的构造函数

我们知道,子类创建的构造函数会默认的调用父类中的无参构造函数。如果父类中没有构造函数,那我就不调用。如果父类中存在有参的构造函数,且没有无参构造函数,子类中的构造函数就必须显式的调用有参构造函数。

我这个程序出错的地方在于,子类中无参的构造函数,在父类没有无参构造函数的前提下,没有显式的调用有参构造函数。

解决方案:显式的调用有参构造函数(初始化列表的方式),或者在父类中声明无参的构造函数,那子类中的构造函数无需做任何操作,就可以默认的调用父类中无参的构造函数。

多继承总结

  • 书写语法就是,继承多个基类,用逗号隔开
  • 同名函数和同名变量的调用需要指定作用域
  • 不建议使用,因为太过复杂,但要了解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值