学习:类和对象——继承

继承:

继承是面向对象三大特性之一

继承的基本语法: class 类名: public/protected/private 父类

总结:

继承的好处:可以减少重复的代码

class A : public B;

A 类称为子类 或 派生类

B 类称为父类 或 基类

派生类中的成员,包含两大部分:

1、一类是从基类继承过来的,一类是自己增加的成员。

2、从基类继承过过来的表现其共性,而新增的成员体现了其个性。


继承方式:

继承的语法:class 子类 : 继承方式 父类

继承方式一共有三种:

1、公共继承
2、保护继承
3、私有继承

相比于其他语言来说,c++还可以自己定义以哪种方式来继承父类,其他语言默认都是public来继承的


继承中的对象模型:

问题:从父类继承过来的成员,哪些属于子类对象中?

示例代码:

class Base
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};

//公共继承
class Son :public Base
{
public:
	int m_D;
};

void test01()
{
	cout << "sizeof Son = " << sizeof(Son) << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

以上我们发现输出的字节为16个字节,但是还不够直观,我们可以通过visual studio相应的工具来查看

使用命令:cl /d1 reportSingleClassLayout查看的类名 所属文件名

结论: 父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到


继承中构造和析构顺序:

子类继承父类后,当创建子类对象,也会调用父类的构造函数

问题:父类和子类的构造和析构顺序是谁先谁后?

示例代码:

#include<iostream>
#include<string>

using namespace std;

class Father {
public:

	Father() {
		cout << "这是父类的构造方法" << endl;
	}

	~Father() {
		cout << "这是父类的析构方法" << endl;
	}
};

class Son :public Father {

public:
	Son() {
		cout << "这是子类的构造方法" << endl;
	}

	~Son() {
		cout << "这是子类的析构方法" << endl;
	}
};

void test01() { //因为我们需要看到析构方法的效果,所以我们在定义一个函数进行使用
	Son s1;
}

int main() {
	test01();
	system("pause");
	return 0;

}

结果:

总结:子类继承父类,先调用父类的构造方法然后再子类的,对于析构来说则顺序相反


继承同名成员处理方式:

问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

1、访问子类同名成员 直接访问即可
2、访问父类同名成员 需要加作用域

示例代码:

#include<iostream>
#include<string>

using namespace std;

class Fat {

public:
	void aaa() {
		cout << "这是父类的aaa函数" << endl;
	}

	void bbb() {
		cout << "这是父类的bbb函数" << endl;
	}

};

class Son:public Fat {

public:
	void aaa() {
		cout << "这是子类的aaa函数" << endl;
	}
};

int main() {
	Son s1;
	s1.aaa(); //这样只能调用子类的aaa函数
	s1.Fat::aaa(); //同名的情况下 通过修改Fat:: 作用域来调用父类的同名函数
	s1.bbb(); //不同名的情况下,可以直接调用父类的函数

	system("pause");
	return 0;

}

总结:

1、子类对象可以直接访问到子类中同名成员
2、子类对象加作用域可以访问到父类同名成员
3、当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数


继承同名静态成员处理方式:

问题:继承中同名的静态成员在子类对象上如何进行访问?

#include<iostream>
#include<string>

using namespace std;

class Fat {

public:
	static int a;
	static void aaa() {
		cout << "这是父类的aaa函数" << endl;
	}

	static void bbb() {
		cout << "这是父类的静态bbb函数" << endl;
	}

};

int Fat::a = 10;

class Son :public Fat {

public:
	static int b;
	static void bbb() {
		cout << "这是子类的静态aaa函数" << endl;
	}
};

int Son::b = 15;

int main() {
	Son s1;
	//通过对象来调用静态函数
	s1.bbb(); // 调用子类的静态函数
	s1.Fat::bbb(); // 同名的情况下,通过作用域来调用
	s1.Fat::aaa(); // 没有同名,可以直接调用父类的静态函数

	//通过类的方式来调用静态函数
	Son::Fat::bbb(); // 第一个::是代表类名访问,第二个::代表访问父类的作用域下

	system("pause");
	return 0;

}

总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名)


多继承语法:

C++允许一个类继承多个类 ps.在java中是不允许一个类继承多个类,但是一个类可以继承多个接口,然后这是课外话

语法: class 子类 :继承方式 父类1 , 继承方式 父类2...

C++实际开发中不建议用多继承,原因是多个继承,多个相同属性的话,都需要添加相应的作用域来进行调用,容易混淆

示例代码:

class Base1 {
public:
	Base1()
	{
		m_A = 100;
	}
public:
	int m_A;
};

class Base2 {
public:
	Base2()
	{
		m_A = 200;  //开始是m_B 不会出问题,但是改为mA就会出现不明确
	}
public:
	int m_A;
};

//语法:class 子类:继承方式 父类1 ,继承方式 父类2 
class Son : public Base2, public Base1 
{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}
public:
	int m_C;
	int m_D;
};


//多继承容易产生成员同名的情况
//通过使用类名作用域可以区分调用哪一个基类的成员
void test01()
{
	Son s;
	cout << "sizeof Son = " << sizeof(s) << endl;
	cout << s.Base1::m_A << endl;
	cout << s.Base2::m_A << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

菱形继承: ps.之前学python的时候菱形继承好像跟这个不太一样emm,到时候再说吧,唉

菱形继承概念:

​两个派生类继承同一个基类,又有某个类同时继承者两个派生类,这种继承被称为菱形继承,或者钻石继承

菱形继承问题:

羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。

草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。

示例代码:

#include<iostream>
#include<string>

using namespace std;

class Animal {
public:
	int age;
};

class Yang:virtual public Animal{};

class Tuo :virtual public Animal{};

class YangTuo:public Yang, public Tuo{};


int main() {
	
	YangTuo ty1;
	ty1.Yang::age = 38;
	ty1.Tuo::age = 28;
	cout << ty1.Yang::age << endl; //直接进行访问不明确,我们需要添加作用域,那我们就需要对父类的age进行赋值
	cout << ty1.Tuo::age << endl;

	cout << ty1.age << endl; //即使进行了上面两步赋值的操作还是不能直接访问当前对象的age,因为编译器还是不知道到底要访问哪一个,通过对virtual对父类进行修饰
	system("pause"); 
	return 0;

}

底层讲解原因参考:https://www.bilibili.com/video/av41559729?p=134

总结:

1、菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
2、利用虚继承可以解决菱形继承问题

原文地址:https://www.cnblogs.com/zpchcbd/p/11867939.html