【C++】继承与派生的概念与理解

继承与派生目录

一.概念

1.基类和派生类

2.继承访问

①继承访问方式

public继承

protected继承

private继承

②继承的作用域

关于重定义

③派生类构造函数和析构函数的调用

3.继承类型

二.定义

1.赋值兼容转换(切割、切片)

2.单继承

3.多继承

4.菱形继承

5.虚拟继承


一.概念

1.基类和派生类

        派生类,又称子类,由一个类继承到另一个类上所得,其除了拥有自身的对象,还拥有从父类那所继承的对象,可以通过一定的方式访问父类的成员对象;基类,又称父类,用于继承到子类,使子类拥有它本身和基类的对象。

        简单地理解,就是爸爸和儿子的关系,爸爸拥有的特征——比如身高,肥胖程度,性格等(基类),都会继承到儿子身上(举个例子),但儿子除了拥有父亲的特征之外,还拥有自己的特征,这些便是派生类。

2.继承访问

①继承访问方式

        我们来看这段代码,将a设置为基类,b设置为派生类,基类public、protected、private中各有一个打印函数,继承的b中则有一个调用基类打印函数的函数。我们将演示三种继承方式时的派生类类中和类外的访问权限。(不写继承方式默认为private)

class a
{
public:
	void public_Print()
	{
		cout << "a::public_Print()" << endl;
	}

protected:
	void protected_Print()
	{
		cout << "a::protected_Print()" << endl;
	}

private:
	void private_Print()
	{
		cout << "a::private_Print()" << endl;
	}
};


// 继承方式
class b : public a
// class b : protected a
// class b : private a
{
public:
    // 派生类类内访问权限测试
	void b_public_Print()
	{
		public_Print();
		protected_Print();
        private_Print();
	}
};

int main()
{
	b B;

    // 派生类类外访问权限测试
    B.public_Print();
	B.protected_Print();
	B.private_Print();

    B.b_public_Print();
	return 0;
}
public继承

        使用public继承时,类内中可以访问基类的public、protected成员,类外仅可以访问public成员,如图:

protected继承

        使用protected继承时,类内中可以访问基类的public、protected成员,此时继承基类的public对象存在于子类的protected中,类外成员都受到保护而不可访问。

private继承

        使用private继承时,类内中可以访问基类的public、protected成员,此时继承基类的public和protected对象存在于子类的private中,类外成员都受到保护而不可访问。图示结果与上图一致。

        基类所有的private对象不管在子类的类内还是类外都被隐藏保护了起来,不可访问。

②继承的作用域

        在继承体系中,基类和派生类都有独立的作用域。

关于重定义

        如果在基类和派生类中都有相同函数名的函数,那么他们会构成重定义,也叫做隐藏,即子类调用该函数会屏蔽对父类同名函数的直接访问,可以通过"基类::基类成员"的方式访问。

③派生类构造函数和析构函数的调用

        在继承中,派生类是必须要调用基类的构造函数的,如果没有调用基类构造函数的话会直接报错(没写的话系统会自动生成默认构造函数)。

        而关于调用顺序:调用构造函数时,会先调用基类的构造函数,然后再调用派生类的;而调用析构函数时,则恰恰相反。(系统默认派生类调用完析构函数后再自动调用基类的)

3.继承类型

        继承中,大部分的数据类型都会被派生类继承,但不包括这一种数据:友元函数,基类的友元函数不会被继承,打个比方,好比你父亲的朋友不一定是你的朋友。还有一个特殊情况:static修饰的静态成员,虽然它可以被继承,但它属于整个类,在该继承体系中始终只存在一个。

二.定义

1.赋值兼容转换(切割、切片)

        基类可以接受派生类的对象,并通过切片达到给基类赋值的目的。我们可以运行以下代码:

class Person
{
public:
	Person(const string& name, const int& age)
		:_name(name)
		, _age(age)
	{}

private:
	string _name;
	int _age;
};

class Student : public Person
{
public:
	Student(const string& name, const int& age, const int& num)
		:Person(name,age)
		,_num(num)
	{}

private:
	int _num;
};

int main()
{
	Student A("张三", 18, 20230724);

	// 切片赋值给父类
	Person B = A;

	return 0;
}

        通过监视窗口可以得到以下情况:

2.单继承

        单继承的概念:一个子类只有一个直接父类时成为单继承。

                    ​​​​​​​        

3.多继承

        多继承的概念:一个子类有两个及以上父类时称这个继承关系为多继承。

                           

         该继承体系中对基类的初始化按照从左到右的顺序进行,析构时则相反。

4.菱形继承

        菱形继承的概念:是多继承的一种特殊情况,派生类继承多个基类时,这几个基类也作为派生类继承了同一个或多个相同的基类。

                ​​​​​​​

        这种情况看似简答,其实比较复杂,会引发很多问题:每个基类对象又都包含了同一个基类对象——数据冗余;有两个相同的基类对象——数据的二义性。

        举例:我们将运行以下代码。

class a
{
public:
	a(const int& num)
		:_a(num)
	{}

private:
	int _a;
};

class b : public a
{
public:
	b(const int& num)
		: _b(num)
		, a(num + 10)
	{}

private:
	int _b;
};

class c : public a
{
public:
	c(const int& num)
		: _c(num)
		, a(num + 10)
	{}

private:
	int _c;
};

class d : public b, public c
{
public:
	d(const int& num1, const int& num2, const int& num3)
		:b(num1)
		,c(num2)
		,_d(num3)
	{}

private:
	int _d;
};

int main()
{
	d D(1, 2, 3);

	return 0;
}

        我们观察运行后的内存情况,可以得出以下图解,二义性和数据冗余可以直观地表达出来。

菱形虚拟继承

        在菱形继承中作为中间节点的子类前加上virtual,可以做到菱形虚拟继承。

class a
{
public:
	a(const int& num)
		:_a(num)
	{}

private:
	int _a;
};

class b : virtual public a
{
public:
	b(const int& num)
		: _b(num)
		, a(num + 10)
	{}

private:
	int _b;
};

class c : virtual public a
{
public:
	c(const int& num)
		: _c(num)
		, a(num + 10)
	{}

private:
	int _c;
};

class d : public b, public c
{
public:
	d(const int& num1, const int& num2, const int& num3, const int& num4)
		:a(num1)
		,b(num2)
		,c(num3)
		,_d(num4)
	{}

private:
	int _d;
};

int main()
{
	d D(1, 2, 3, 4);

	return 0;
}

        用此方法得到将不再创建重复的_a,而是创建一个虚基表指针,虚基表里面存储着偏移量,系统通过虚基表里的偏移量找到_a所存储的地方,此处虚基表的大小为8个字节,因为不一定只会有一个需要用偏移量标记的数据。

​​​​​​​

猜你喜欢

转载自blog.csdn.net/qq_74641564/article/details/131899970
今日推荐