C++之继承。

前面学习了C++的第一个特性封装,这次就来学一学这第二个特性C++的继承。

一。继承的相关概念

1.概念:继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。

2.简单举例:

#include<iostream>
using namespace std;

class Person           //定义一个类
{
public:
	char* _name;
	char* _sex;
	int _age;

public:
	void show()
	{
		cout<<_name<<_sex<<_age<<endl;
	}

};

class Student : public Person      //公有继承
{
public:
	void play()
	{
		cout<<"good"<<endl;
	}
};

int main()
{
	Person p;
	p._name="李四";
	p._sex="男";
	p._age=18;
	p.show();

	Student s;           //第二个类就可以调用第一个类里的成员了。
	s._name="张三";
	s._sex="男";
	s._age=18;
	s.show();
	system("pause");
	return 0;
}

3.继承的基本格式:

class 新的类名(派生类) : 继承权限    已经建好的类名(基类)

二。继承权限

1.继承权限共有是三种,分别是:公有继承、保护继承以及私有继承。

2.区别与联系:

3.注意问题:

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

(2)public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。

(3)protected/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的都是公有继承。私有继承意味着is-implemented-in-terms-of(是根据……实现的)。通常比组合(composition)更低级,但当一个派生类需要访问基类保护成员或需要重定义基类的虚函数时它就是合理的。

(4)不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员, 基类的私有成员存在但是在子类中不可见(不能访问)。

(5)使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
(6)在实际运用中一般使用都是public继承,极少场景下才会使用protetced/private继承。

三。赋值兼容规则

1.子类对象可以赋值给父类对象(切割/切片)。

2. 父类对象不能赋值给子类对象。

3. 父类的指针/引用可以指向子类对象。

4.子类的指针/引用不能指向父类对象(可以通过强制类型转换完成,但不能调用成员函数,会崩溃)。

举例:

#include<iostream>

using namespace std;

class A
{
public:
	void show()
	{
		cout<<_name<<endl;
	}
protected:
	char* _name;
};

class B:public A
{
public:
	int num;

};

int main()
{
	A a;
	B b;
	// 1.子类对象可以赋值给父类对象
	/*a=b;*/
	// 2.父类对象不能赋值给子类对象
	/*b=a;*/

	// 3.父类的指针/引用可以指向子类对象
	A* p1=&b;
	A& p2=b;

	//4.子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)
	B* p3=(B*)&a;
	B& p4=(B&)a;

	system("pause");
	return 0;
}

四。继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域。
2.子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问。(在子类成员函数中,可以使用基类::基类成员访问) --隐藏 --重定义
3. 注意在实际中在继承体系里面最好不要定义同名的成员。

五。派生类的默认成员函数

1.就和类的六大默认成员函数一样,分别是:构造函数、析构函数、拷贝构造函数、赋值运算符重载、const修饰操作符重载以及取地址操作符重载。

2.生成和合成:当没有显式定义时编译器会自动生成这6个默认成员函数,而这里则是编译器会合成这6个默认成员函数。

生成:不依赖于任何东西,只是编译器根据类的定义简单生成基于基础类型的默认成员函数。
合成:必须依赖于基类,编译器根据基类的相应成员函数的行为来合成派生类的默认成员函数。

从而得到下图的调用顺序:

注:

a)基类没有缺省构造函数,派生类必须要在初始化列表中显式给出基类名和参数列表。
b)基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省构造函数。
c)基类定义了带有形参表构造函数,派生类就一定定义构造函数。

3.这里有一个经典面试题:

实现一个不能被继承的类:(私有构造函数+静态方法实例化与释放)。

将其构造函数声明为私有的,这样就可以阻止子类构造对象了。
class A
{
public:
	static A* get(int a)
	{
		A* p=new A;
		p->num=10;
		cout<<"num is"<<p->num<<endl;
		return p;
	}

	static void* destory(A* p)
	{
		if(p!=NULL){
		delete p;
		p=NULL;
		}
	}

private:
	A()
		:num(10)
	{}

public:
	int num;
};

class B : public A
{
public:
	void show()
	{
		cout<<num<<endl;
	}
};
int main()
{
	B b;
	b.show();
	

	system("pause");
	return 0;
}

六。继承和友元

1.首先我们要谨记友元关系不能被继承,也就是基类的友元不能访问子类的私有和保护成员。

2.举例:

class A
{
	friend void show(A& a,B& b);
	string _birthday;
protected:
	string _name;
private:
	int _age;
};

class B : public A
{
public:
	int n1;
protected:
	int n2;
private:
	int n3;
	void show(A& a,B& b)
	{
		cout << a._birthday << endl;    
		cout <<a._name<< endl ;
		cout <<a._age<< endl ;
		cout <<b.n1<<endl;
		cout <<b._name<< endl ;
		cout <<b.n2<< endl ;
		cout <<b.n3<< endl ;
	}
};

int main()
{
	A a;
	B b;
	b.show(a,b);
	system("pause");
	return 0;
}

七。继承与静态成员

1.基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。

八。继承下的派生类对象模型

1.它分为单继承、多继承、菱形继承以及菱形虚拟继承。

2.虚拟继承:它的格式为:class 派生类名 :virtual 继承权限  基类名

(1)虚拟继承--解决菱形继承的二义性和数据冗余的问题。
(2)虚拟继承在继承权限前加上virtual关键字即可构成虚拟继承。
(3)虚继承的特点是,在任何派生类中的virtual基类总用同一个(共享)对象表示。

3.虚继承和直接继承有什么区别。

a)时间:在通过继承类对象访问虚基类对象中的成员(包括数据成员和函数成员)时,都必须通过某种间接引用来完成,这样会增加引用寻址时间(就和虚函数一样),其实就是调整this指针以指向虚基类对象,只不过这个调整是运行时间接完成的。
b)空间:由于共享所以不必要在对象内存中保存多份虚基类子对象的拷贝,这样较之 多继承节省空间。虚拟继承与普通继承不同的是,虚拟继承可以防止出现菱形继承时,一个派生类中同时出现了两个基类的子对象。也就是说,为了保证 这一点,在虚拟继承情况下,基类子对象的布局是不同于普通继承的。因此,它需要多出一个指向基类子对象的指针。

以上就是C++继承的基本内容了。

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值