继承详解:

一、继承:

1.1

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

1.2三中继承方式图解:

在这里插入图片描述
(1)保护成员在继承时等同于公有成员,在类外访问等同于私有属性。
(2)基类私有成员在派生类中“不可见”
(3)使用继承, 我们的继承方式一般都是public继承, 因为其他继承方式, 没法复用代码,代码利用率低。

1.3具有继承关系的内存分布

隐藏父对象:就是在子类继承父类时,他是会在子类中构建一个隐藏的父对象,至于与它是什么方式继承无关,只要是继承就会生成该对象,此文中会大量使用隐藏父对象

1.3.1分布情况:

#include<iostream>

using namespace std;
class Base
{
private:
	int a;
	int b;
};
class Derived:public Base
{

};
int main()
{
	cout << sizeof(Base) << endl;
	cout << sizeof(Derived) << endl;
}

在这里插入图片描述
继承时,会继承基类的一切成员。

1.3.2不可见性:

派生类私有继承的基类私有成员“不可见”,无法访问。

#include<iostream>

using namespace std;
class A
{
private:   int ax;
protected:  int ay;
public:    int az;
public:
	A() {
		ax = ay = az = 0;
	}
};
class B:public A
{
private:  int bx;
protected: int by;
public: int bz;
public: B() { bx = by = bz = 1; }
	  void fun()
	  {
		  ay = 20; az = 30;//ax在此不可访问,不可见。
	  }

};
int main()
{
	B b;
	b.fun();
}

子类对象在构建时会构建一个隐藏父对象。

**此时内存分布如下:**上低下高
在这里插入图片描述
在这里插入图片描述

1.3.3隐藏无名对象和类内对象

对于继承而来的无名隐藏对象和 成员对象来说,最大的区别在于保护成员的访问问题:
无名对象A只有私有成员不可访问,而成员有名对象aa保护成员也不可访问。

using namespace std;
class A
{
private:   int ax;
protected:  int ay;
public:    int az;
public:
	A() {
		ax = ay = az = 0;
	}
};
class B:public A
{
private:  int bx;
protected: int by;
public: int bz;
 A aa;
public: B() { bx = by = bz = 1; }
	  void fun()
	  {
		  ay = 20; az = 30;
	  }

};
int main()
{
	B b;
	b.fun();
}

在这里插入图片描述

1.3.4同名隐藏:

大家对c的同名隐藏并不陌生,继承过程也具有同名隐藏的特性

#include<iostream>

using namespace std;
class A
{
protected:  int ax;
public:A() :ax(0) {}
};
class B
{
private: int ax;
public:
	B():ax(10){}
	void fun()
	{
		ax = 100;
	}
};
int main()
{
	B b;
	b.fun();
}

在这里插入图片描述

2.1赋值兼容性规则:

2.1.1、切片问题:

可以通过子对象给父对象赋值,因为子对象会构建无名隐藏父对象,赋值时可以切片赋值;不能通过父对象给子对象赋值。

#include<iostream>

using namespace std;
class Object
{
private: int value;
public:
	Object(int x = 0) :value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destroy Object:" << this << endl;
	}

};
class Base:public Object
{
private:  int num;
public:
	Base(int x = 0) :num(x), Object(x + 10)
	{
		cout << "Create Base" << this << endl;
	}
	~Base()
	{
		cout << "Destory Base" << this << endl;
	}
};
int main()
{
	Object obja(100);
	Base base(10);
	obja = base;
}

如图:
在这里插入图片描述

在这里插入图片描述

2.1.2:引用和赋值:

不能用父类指针指向子对象
在这里插入图片描述

在这里插入图片描述
类型对指针的约束有两个方面:1.指针+1的能力
2.类型解析的能力
父类指针指向子类对象,受类型的约束,只能读取到隐藏父对象的信息

引用也是同理,只有引用父对象的信息,但可以使用,在下文拷贝构造继承会有使用
在这里插入图片描述

3.1、继承关系中的构造函数,析构函数,拷贝函数,赋值函数。

3.1.1、
由上文切片问题处可知构造函数,析构函数可以继承。
在这里插入图片描述
构建子类对象时,会先构造隐藏父类对象,在此基础上构建出子类对象。

3.1.2、拷贝构造的继承

拷贝构造的继承方式大有不同,下边便来看看拷贝构造到底怎么继承。

#include<iostream>

using namespace std;
class Object
{
private: int value;
public:
	Object(int x = 0) :value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destroy Object:" << this << endl;
	}
	Object(const Object& obj):value(obj.value)
	{
		cout << "Copy Create Object" << this << endl;
	}

};
class Base:public Object
{
private:  int num;
public:
	Base(int x = 0) :num(x), Object(x + 10)
	{
		cout << "Create Base" << this << endl;
	}
	~Base()
	{
		cout << "Destory Base" << this << endl;
	}
	Base(const Base& bas):num(bas.num)
	{
		cout << "Copy Create Base" << this << endl;
	}
};
int main()
{
	Base base(10);
	Base base1(base);
	
}

在这里插入图片描述
由此可知,在子类对象拷贝构造时,只会调用子类的拷贝构造函数,而由内存分布可知,子类对象含有隐藏父对象,那不调用父类对象的拷贝构造,如何来构建拷贝后对象的隐藏父对象,在图中调用的是父类对象的构造函数来构建该隐藏父对象。那这样的构造是否会出现问题呢?
在这里插入图片描述
由图可知,这样的拷贝方式会出现错误,它构建的拷贝对象的隐藏父对象和被拷贝对象的隐藏父对象不同。那么我们要怎么才能调用父类的拷贝构造来构建该隐藏父对象呢?

其实,也是很简单的,我们只需使用列表的方式加上对其的构建,使其在拷贝构造子对象时,必须先去拷贝构造隐藏父对象。

在这里插入图片描述
由赋值兼容性规则我们可以知道,可以通过父类对象引用子类对象,而正好可以匹配父类的拷贝构造函数。
在这里插入图片描述
这种方式就可以来调用父类的拷贝构造。

至于说要是不通过列表方式初始化,而是直接写入子类拷贝里边可不可以呢?
在这里插入图片描述

在这里插入图片描述
由运行结果就可以看出,写在子类内没有任何用处,它是有对父类对象的拷贝,但是他是重新生成的一个无名对象,和要拷贝的隐藏父对象毫无关系(由this指针的地址就可以看出),并且它在构建后立马析构。所以该种方式并不可取。

3.1.3、赋值语句的继承

#include<iostream>

using namespace std;
class Object
{
private: int value;
public:
	Object(int x = 0) :value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destroy Object:" << this << endl;
	}
	Object(const Object& obj):value(obj.value)
	{
		cout << "Copy Create Object" << this << endl;
	}
	Object& operator =(const Object& obj)
	{
		if (this != &obj)
		{
			value = obj.value;

		}
		cout << "Object::operator=:" << this << endl;
		return *this;
		
	}

};
class Base:public Object
{
private:  int num;
public:
	Base(int x = 0) :num(x), Object(x + 10)
	{
		cout << "Create Base" << this << endl;
	}
	~Base()
	{
		cout << "Destory Base" << this << endl;
	}
	Base(const Base& bas):num(bas.num),Object(bas)
	{
		cout << "Copy Create Base" << this << endl;
	}
	Base& operator =(const Base& bas)
	{
		if (this != &bas)
		{
			num = bas.num;

		}
		cout << "Base::operator=:" << this << endl;
		return *this;

	}
};
int main(){
	Base basea(10);
	Base baseb(20);
	basea = baseb;
	
}

在这里插入图片描述
由图可知,和拷贝函数相同,并没有调动父类的赋值语句,那结果会不会出现错误呢?
在这里插入图片描述
未调用父类的赋值语句结果出现错误,basea的value值不等于baseb的value,那如何可以调动父类的赋值语句呢?可能有的人会认为这里也调用拷贝构造那样的列表方式就可以了,其实不能这样做,我们要记得:只有构造和拷贝构造由列表初始化的方式,其他函数不能

我们的方式也很简单,加入强转即可。
在这里插入图片描述
我们把base类型的指针直接强转为Object类型,就可以扩大他的解析范围,从而可以完美赋值。
在这里插入图片描述
在这里插入图片描述

在这类问题中,我们一定要注意赋值兼容性的问题。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值