C++面向对象(九)继承

一、基础概念
1.继承本质:实际上为了代码的复用;
2.类和类之间的基本关系:
2.1 代理:如STL中的容器适配器,被代理类的接口的功能的子集
2.2 组合:A类是B类的一部分
2.3 继承:A类是B类的一种
3.派生类和基类
基类我们也叫父类,派生类也叫子类,子类继承自父类,父类派生了子类。

二、不存在多态情况时对象内存布局

// 基类
class base
{
public:
	int ma;
protected:
	int mb;
private:
	int mc;
};

//公有继承,当不写public,默认的是private继承
//默认和类的访问限定一样,都是私有的,一般都使用public继承
//派生类
class dev:public base   
{
public:
	int md;
protected:
	int me;
private:
	int mf;
};

1.分别定义一个子类对象和基类对象,分析内存布局:
在这里插入图片描述

sizeof(b) = = 12
sizeof(d) ==  24

子类继承了父类哪些东西:
除了构造函数和析构函数以外的所有关系,包括基类的作用域和父类私有的成员,因为继承父类的构造和析构函数没什么用;
那也就是说,我在子类中可以定义与父类同名的成员变量和方法,因为我的作用域是不是同的。

base::void fun();
dev::void fun();
base::int ma;
dev::int mb;
不要在意这里写法对不对,为了说明问题

三、不同子类继承方式继承到父类的变量和方法访问权限:
1.三种访问限定的符的含义:

public:公开的,表示数据对用户的是公开的,都可以访问
protected:
保护的,虽然是保护的,但是对自己的子女和朋友(friend)是公开的,外部类不能访问
private:私有的,除自己和友元类都不能访问,因为只属于我本身。

2.不同继承方式带来不同访问权限:
public继承:
在子类能访问父类的public和protected成员,不可访问父类private,但是会继承下来;
在外部可以访问继承来父类public成员,但是不能访问protected和private;

//基类
class base
{
public:
	int ma;
protected:
	int mb;
private:
	int mc;
};
//派生类,public继承
class dev:public base   
{
public:
	void show()
	{   //public继承中,子类中可以访问继承来父类的public和protected
		cout << ma << endl;
		cout << mb << endl;
	}
	int md;
protected:
	int me;
private:
	int mf;
};


int main()
{	
	dev d;
	//public继承中,类外可以访问继承来父类的public
	cout << d.ma << endl;
	return 0;
}

protected继承:
在子类中,原来父类的公开度比protected高的成员会被降级为protected,也就是父类成员到了子类中,开放度最大是protected,public成员降级为protected,其他不变。

//基类
class base
{
public:
	int ma;
protected:
	int mb;
private:
	int mc;
};
//派生类,保护继承
class dev:protected base   
{
public:
	void show()
	{   //protected继承中,子类中可以访问继承来父类的public和protected
		//只不过会把基类中原本public降级为protected,protected依然可以自己访问
		cout << ma << endl;
		cout << mb << endl;
	}
	int md;
protected:
	int me;
private:
	int mf;
};


int main()
{	
	dev d;
	//此时类外不能访问子类继承来父类的任何东西,因为是protected
	return 0;
}

private继承:
这样把父类的public成员和protected降级为private,只能子类自己访问,类外的不能访问。

// 基类
class base
{
public:
	int ma;
protected:
	int mb;
private:
	int mc;
};

//派生类,私有继承
class dev:private base   
{
public:
	void show()
	{   //private继承中,子类中可以访问继承来父类的public和protected
		//只不过会把基类中原本public降级为private,private依然可以对自己可见啊
		cout <<ma << endl;
		cout <<mb << endl;
	}
	int md;
protected:
	int me;
private:
	int mf;
};


int main()
{	
	dev d;
	//此时类外不能访问子类继承来父类的任何东西,因为是private
	return 0;
}

3.总结设计类的原则:

如果成员要对外部公开:public
如果成员要对只对子类公开:protected
如果成员只对自己公开,对外部和子类限定访问:private

四、派生类对象的构造方式

从一开始的内存布局来看,基类数据存放在派生类对象的内存前,所以我应该先构造基类部分。
如何调用在派生类中调用基类构造函数呢?
在派生类的构造函数初始化列表中调用。

实例:

class base
{
public:
	base(int a) :ma(a){ cout << "base(int a)" << endl; }
	~base(){ cout << "~base()" << endl; }
protected:
	int ma;

};

class dev:public base   
{
public:
	dev(int data) :base(data), mb(data){ cout << "dev(int data)" << endl; }
	~dev(){ cout << "~dev()" << endl; }

private:
	int mb;
};

int main()
{	
	dev d(10);
	return 0;
}

执行结果:
base(int a)
dev(int data)
~dev()
~base()
请按任意键继续. . .

从执行结果我们不难发现,构造一个派生类的对象的过程:
1.先调用基类的构造函数,初始化基类部分的内存;
2.调用派生类的构造函数,初始化派生类部分的内存;
3.析构时,先析构派生类部分,再析构基类部分。
(先构造的后析构,后构造的先析构)
注意事项:
如果不自己在派生类调用基类的构造函数,系统会自动帮我们调用默认的基类构造函数,前提是我们没有提高自定义构造函数,系统能帮我们产生构造函数。

五、基类和派生类同名成员方法的三种关系:

1.重载(overload):相同作用域下,同名不同参,与返回值无关;
2.隐藏(overhide):父类和子类,父类某个方法和子类的名字相同,就会被隐藏;
3.覆盖(override):下篇文章我将讲多态,会说到。

重载:base类中两个show方法就是重载关系

class base
{
public:
	base(int a) :ma(a){}
	~base(){}
	void show(){ cout << "base::show()" << endl; }
	void show(int i){ cout << "base::show(int)" << endl; }    
protected:
	int ma;
};

隐藏:子类的show会隐藏父类中的 两个 show方法,包括参数不同的show(int)

class base
{
public:
	base(int a) :ma(a){}
	~base(){}
	void show(){ cout << "base::show()" << endl; }
	void show(int i){ cout << "base::show(int)" << endl; }
protected:
	int ma;
};
class dev:public base   
{
public:
	dev(int data) :base(data), mb(data){}
	~dev(){}
	void show(){ cout << "dev::show()" << endl; }
private:
	int mb;
};


int main()
{	
	dev d(10);
	d.show();
	return 0;
}
执行结果:
dev::show()
请按任意键继续. . .

我们发现调用的是dev作用域下的show(),是因为把基类的show隐藏了

如果我们一定要调用基类的show方法该怎么做:

int main()
{	
	dev d(10);
	//基类的方法是被继承下来了,只要指定基类的作用域就可以调用
	d.base::show();
	return 0;
}
执行结果:
base::show()
请按任意键继续. . .

同理,如果成员变量也有同名的,同样会构成隐藏
访问时默认是子类变量,访问父类可以讲父类的作用域

六、派生类从下到上转换为基类:

1.基类对象赋值给派生类对象:ERROR
2.派生类对象赋值给基类对象:OK
3.派生类指针(引用)指向基类对象:ERROR,存在越界问题
4.基类指针(引用)指向派生类对象:OK,虽然可以,但是访问不到派生类内存
(多态会解决这个问题)

如果你不懂为什么对还是为什么错我给你举个例子
基类:人
派生类:男人 || 女人

我可说男人是人的一种,也可以说女人是人的一种
但是我不能说人就是男人。

这样会有什么问题:

int main()
{	
	dev d(10);
	base *p = &d;  //基类指针指向派生类对象
	p->show();
	//汇编上: call        base::show 
	return 0;
}

执行结果:
base::show()
请按任意键继续. . .

调用基类的show,而不是派生类的
很奇怪,为什么我一个派生类对象要去调用基类的show的
原因分析:
指针的类型决定的了指针的“管辖范围”,base类的指针指向派生类对象,
只能管得了sizeof(base对象)那么大,所以就没法看到派生类的方法和变量了。
如图:

在这里插入图片描述
指针p指针只能看到红色部分,而看不到黑色部分。

虚函数的出现解决了这个问题,见下一篇文章。

猜你喜欢

转载自blog.csdn.net/KingOfMyHeart/article/details/90136791
今日推荐