c++ 继承

继承

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

类与类之间存在特殊的关系,定义一些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。

利用继承的技术,减少重复的代码。

当我们进入一个网站后,以编程网页为例。其中有着公共的头部,公共的尾部,公共的列表。在网页中间,拥有各个编程语言的视频。不使用继承会让代码多出许多重复的内容。

#include <iostream>
using namespace std;

//普通页面

//C++页面
class CPP
{
public:
	void header()//公共头部
	{
		cout << "公共头部:    首页、公开课、登录、注册..." << endl;
	}
	void footer()
	{
		cout << "公共尾部:    帮助中心、 交流合作、 站内信息..." << endl;
	}
	void left()
	{
		cout << "公共分类列表:Java、Python、C++..." << endl;
	}
	void content()
	{
		cout << "C++学科视频" << endl;
	}
};

void test01()
{
	cout << "C++下载视频页面如下:" << endl;
	CPP cpp;
	cpp.header();
	cpp.footer();
	cpp.left();
	cpp.content();
}

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

在上面的代码中,写了一个C++类。C++类中包含了公共头部、公共尾部、公共列表,还有C++的教学视频。

在网站中不仅仅只包含了C++,还包含了Python、Java等编程语言。因此在代码中添加新的类。可以完全使用复制粘贴的方式,因为他们仅仅只有内部的content模块不同。

class Python
{
public:
	void header()//公共头部
	{
		cout << "公共头部:    首页、公开课、登录、注册..." << endl;
	}
	void footer()
	{
		cout << "公共尾部:    帮助中心、 交流合作、 站内信息..." << endl;
	}
	void left()
	{
		cout << "公共分类列表:Java、Python、C++..." << endl;
	}
	void content()
	{
		cout << "Python学科视频" << endl;
	}
};

//JAVA页面
class Java
{
public:
	void header()//公共头部
	{
		cout << "公共头部:    首页、公开课、登录、注册..." << endl;
	}
	void footer()
	{
		cout << "公共尾部:    帮助中心、 交流合作、 站内信息..." << endl;
	}
	void left()
	{
		cout << "公共分类列表:Java、Python、C++..." << endl;
	}
	void content()
	{
		cout << "Java学科视频" << endl;
	}
};

写起来很快,因为大部分代码都相同,但是网页中不仅仅只有这三种编程语言,复制粘贴写起来很舒服,但是,如果被你的上司看到,他只会认为你是一个菜鸟,你写的代码太low了。

利用继承实现页面。

#include <iostream>
using namespace std;

//利用继承实现页面

//公共页面类
class basePage
{
public:
    void header()//公共头部
    {
        cout << "公共头部:    首页、公开课、登录、注册..." << endl;
    }
    void footer()
    {
        cout << "公共尾部:    帮助中心、 交流合作、 站内信息..." << endl;
    }
    void left()
    {
        cout << "公共分类列表:Java、Python、C++..." << endl;
    }
};

//C++页面
class CPP :public basePage
{
public:
    void content()
    {
        cout << "C++学习视频" << endl;
    }
};

//JAVA页面
class Java :public basePage
{
public:
    void content()
    {
        cout << "Java学习视频" << endl;
    }
};

//Python页面
class Python :public basePage
{
public:
    void content()
    {
        cout << "python学习视频" << endl;
    }
};

void test01()
{
    CPP cpp;
    cpp.header();
    cpp.footer();
    cpp.left();
    cpp.content();
}

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

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

1、继承的基本语法

继承的基本语法其实就一条, class 子类:继承方式 父类。 例如上面代码中的 class CPP : public basePage

子类又称为派生类,父类又称为基类。 子类中的成员包含两部分:一部分是从基类继承过来的,另一部分是自己增加的成员。 从基类继承的表现共性,新增的表现个性。

2、继承方式

继承的方式一共有三种:公共继承、保护继承和私有继承。

在类中同样有这三种类型。父类中的私有属性,无论子类以什么方式继承,都不可访问。

如果子类共有继承父类(class B : public A),成员属性不变,父类中是公有依然公有;保护依然是保护;私有属性不可访问。

class Base1
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class son1 : public Base1
{
public:
	void func()
	{
		m_A = 10; //父类公共成员依旧是公共权限。
		m_B = 10; //保护权限依旧是保护权限。
		m_C = 10;//标红,私有权限子类访问不到
	}
void test01()
{
	son1 s1;
	s1.m_A = 100;
	s1.m_B = 100; //标红 保护权限类外访问不到。
}
};

如果子类保护继承父类(class B : protected A),子类中不存在公有属性,父类中的共有属性继承后变为保护属性,保护属性依旧是保护属性,私有属性不可访问。

class Base2
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class son2 : protected Base2
{
public:
	void func() 
	{
		m_A = 100;
		m_B = 100;
		m_C = 100; // 标红,子类访问不到。
	}
};

void test02()
{
	son2 s2;
	s2.m_A = 1000; //变为保护权限,类外访问不到
	s2.m_B = 1000;
}

如果子类私有继承父类(class B : private A),所有成员属性都变成私有,父类中的原私有属性不可访问。

3、继承中的对象模型

问题:从父类继承的成员,那些属于子类对象?

父类中,所有的非静态成员属性都会被继承。

#include <iostream>
using namespace std;

//继承中的对象模型
class Base
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class son : public Base
{
	int m_D;
};

void test01()
{
	//父类中,所有的非静态成员属性都会被继承
	//私有成员属性 被编译器隐藏了,因此访问不到,但是确实被继承了。
	cout << "size of son = " << sizeof(son) << endl;
}

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

代码运行结果是 16, 所以父类中,所有的非静态成员属性都会被继承。

私有成员属性 被编译器隐藏了,因此访问不到,但是确实被继承了。可以利用开发人员命令提示工具产看对象模型。

4、继承中的构造和析构顺序

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

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

#include <iostream>
using namespace std;

//继承中的构造和析构顺序

class Base
{
public:
	Base()
	{
		cout << "Base构造函数!" << endl;
	}
	~Base()
	{
		cout << "Base析构函数!" << endl;
	}
};

class Son : public Base
{
public:
	Son()
	{
		cout << "Son构造函数!" << endl;
	}
	~Son()
	{
		cout << "Son析构函数!" << endl;
	}
};

void test01()
{
	//Base b;
	Son s;
}

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

运行代码:“Base构造函数! Son构造函数! Son析构函数! Base析构函数!

继承中先调用父类构造函数,在调用子类构造函数,析构函数与构造函数相反。

5、继承中同名成员处理方式

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

访问子类成员,直接访问; 访问父类同名成员,需要加上作用域。

同名成员属性:

#include <iostream>

using namespace std;

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


class Son : public Base
{
public:
	Son()
	{
		m_A = 200;
	}

	int m_A;

};

void test01()
{
	Son s;
	cout << "Son下的m_A = " << s.m_A << endl;
	cout << "Base下的m_A = " << s.Base::m_A << endl;
}

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

s.m_A 访问到子类中的成员m_A.

s.Base::m_A 加了作用域Base:: ,访问到父类Base中的m_A。

同名成员函数:和同名成员变量一样,加上作用域。

    s1.func();
	s1.Base::func();

那么,当父类中出现了重载函数怎么调用呢?

class Base
{
public:
	Base()
	{
		m_A = 100;
	}

	void func()
	{
		cout << "Base-func()调用 " << endl;
	}
	void func(int a)
	{
		cout <<"Base-func(int a)调用 " << endl;
	}

	int m_A;
};

class Son : public Base
{
public:
	Son()
	{
		m_A = 200;
	}

	void func()
	{
		cout << "Son-func()调用 " << endl;
	}

	int m_A;

};

根据上面的内容,我们可以想到使用

s1.Base::func(100);

来调用。 那我们可不可以直接

s1.func(100);

来调用呢? 答案是不可以的,当我们这么写时,这段代码会被直接标红,原因就是:如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中的所有同名成员函数。也就是说,所有的重载,只要时同名,都被隐藏了。 此时我们只能加作用域。

6、同名静态成员处理

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

与非静态成员处理方式一致。

举一反三。

cout << "Son 下 m_A = " << s.m_A << endl;
cout << "Base 下 m_A = " << s.Base::m_A << endl;

这是访问同名静态成员的方式。 但是静态成员有两种访问方式,这只是其中的一种:.通过对象来访问。 还可以通过类名来访问。

cout << "Son 下 m_A = " << Son::m_A << endl;
//第一个 :: 代表通过类名的方式来访问, 第二个 :: 代表访问父类作用域下。
cout << "Base 下 m_A = " << Son::Base::m_A << endl;

调用同名静态函数方式相同(重载函数也相同)。

s1.func();
s1.Base::func();

Son::func();
Son::Base::func();

完整代码:

#include <iostream>
using namespace std;

class Base
{
public:
	static int m_A;

	static void func()
	{
		cout << "Base-static void func()调用" << endl;
	}
};

//初始化
int Base::m_A = 100;

class Son: public Base
{
public:
	static int m_A;
	static void func()
	{
		cout << "Son-static void func()调用" << endl;
	}
};
int Son::m_A = 200;

//静态属性
void test01()
{
	//1.通过对象来访问
	cout << "通过对象来访问" << endl;
	Son s;
	cout << "Son 下 m_A = " << s.m_A << endl;
	cout << "Base 下 m_A = " << s.Base::m_A << endl;

	//2.通过类名来访问
	cout << "通过类名来访问" << endl;
	cout << "Son 下 m_A = " << Son::m_A << endl;
	//第一个 :: 代表通过类名的方式来访问, 第二个 :: 代表访问父类作用域下。
	cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}

//静态函数
void test02()
{

	Son s1;
	//1.通过对象访问
	cout << "通过对象访问" << endl;
	s1.func();
	s1.Base::func();

	//2.通过类名访问
	cout << "通过类名访问" << endl;
	Son::func();
	Son::Base::func();
}


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

7、多继承语法

C++允许一个类继承多个类。 语法:class 子类 :继承方式 父类1,继承方式 父类2... 多继承可能会引发同名成员出现,需要加作用域区分。 C++实际开发不建议多使用。

#include <iostream>
using namespace std;

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

	int m_A;
};

class Base2
{
public:
	Base2()
	{
		m_A = 200;
	}

	int m_A;
};

class Son : public Base1, public Base2
{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}

	int m_C;
	int m_D;
};

void test01()
{
	Son s;
	cout << sizeof(s) << endl; //结果为16。
	cout << "Base1::m_A = " << s.Base1::m_A << endl;
	cout << "Base2::m_A = " << s.Base2::m_A << endl;

}

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

8、菱形继承

概念: 两个派生类继承同一个基类; 某个类同时继承两个派生类; 又叫钻石继承。

#include <iostream>
using namespace std;

//动物类
class Animal
{
public:
	int m_Age;
};
//数据只需要一份,菱形继承导致数据有两份,资源浪费。
//利用虚继承 解决菱形继承的问题
//在继承前加关键字 virtual 变为虚继承
//Animal类成为 虚基类
//羊类
class Sheep : virtual public Animal {};
//驼类
class Tuo : virtual public Animal {};
//羊驼类
class SheepTuo : public Sheep, public Tuo {};

void test01()
{
	SheepTuo st;
	st.Sheep::m_Age = 18;
	st.Tuo::m_Age = 28;
	//当菱形继承,两个父类拥有相同数据,需要加作用域区分。
	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age <<endl;
	cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
	cout << st.m_Age << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
全部评论

相关推荐

头像
10-09 19:35
门头沟学院 Java
洛必不可达:java的竞争激烈程度是其他任何岗位的10到20倍
点赞 评论 收藏
分享
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务