C++(九、继承)

c++中的继承

1、继承的概念和定义
2、基类和派生类对象的赋值转换
3、继承中的作用域
4、派生类的默认成员函数
5、继承与友元
6、继承与静态成员
7、复杂的菱形继承

——————————————————————————————

1、继承的概念和定义

继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加功能,这样产生新的类,称派生类。

继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。

以前我们接触的复用都是函数复用,继承是类设计层次的复用。

基类的成员变量的无论是什么,都会继承到派生类中,但是 ,派生类不一定能使用继承的成员变量。

继承关系和访问限定符
在这里插入图片描述

继承基类成员访问方式的变化

继承权限–public
基类中public/protected的成员在子类中权限不变
基类中private的成员在子类中不可见(不能用—该成员变量确实已经继承到子类中)
不可见—> 派生类对继承的私有成员不能使用,即使在派生类内部,

在这里插入图片描述
继承方式–protected
基类中public的成员在子类中访问权限已经变成protected
基类中protected的成员在子类中访问权限不变
基类中private的成员在子类中不可见(不能用—该成员变量确实已经继承到子类中)

在这里插入图片描述

继承方式–private
基类中public的成员在子类中访问权限已经变成private
基类中protected的成员在子类中访问权限已经变成private
基类中private的成员在子类中不可见(不能用—该成员变量确实已经继承到子类中)
在这里插入图片描述
class 和 struct的默认继承权限

class默认继承权限—priavet

struct默认继承权限—public

继承权限的验证



#include <iostream>
using namespace std;

#if 0
class Base
{
public:
	void SetBase(int pri, int pro, int pub)
	{
		_pri = pri;
		_pro = pro;
		_pub = pub;
	}

	void PrintBase()
	{
		cout << _pri << endl;
		cout << _pro << endl;
		cout << _pub << endl;
	}

private:
	int _pri;
protected:
	int _pro;
public:
	int _pub;
};

class Derived : public Base
{};

int main()
{
	// 可以验证:派生类将基类中的成员变量继承到子类中
	cout << sizeof(Derived) << endl; //12 ---> 将基类的成员变量继承到子类中

	// 验证:基类中的成员函数是否被子类继承
	Derived d;
	d.SetBase(10, 20, 30);
	d.PrintBase();//
	return 0;
}
#endif

#if 0
// public继承方式:

class Base
{
public:
	void SetBase(int pri, int pro, int pub)
	{
		_pri = pri;
		_pro = pro;
		_pub = pub;
	}

	void PrintBase()
	{
		cout << _pri << endl;
		cout << _pro << endl;
		cout << _pub << endl;
	}

private:
	int _pri;
protected:
	int _pro;
public:
	int _pub;
};

// 继承权限--public
// 基类中public/protected的成员在子类中权限不变
// 基类中private的成员在子类中不可见(不能用---该成员变量确实已经继承到子类中)
class Derived : public Base
{
public:
	void SetDerived(int priD, int proD, int pubD)
	{
		_priD = priD;
		_proD = proD;
		_pubD = pubD;

		_pro = 10;
		// _pri = 20; // 编译报错,在派生类中不能访问基类中私有的成员变量
	}

	// 访问权限:限定该成员变量是否可以直接在类外进行调用
public:
	int _pubD;
protected:
	int _proD;
private:
	int _priD;
};

class D : public Derived
{
public:
	void Test()
	{
		_pro = 10;
	}
};

int main()
{
	Derived d;
	cout << sizeof(d) << endl;

	d._pub = 10;
	// d._pro = 10;
	return 0;
}
#endif


#if 0
class Base
{
public:
	void SetBase(int pri, int pro, int pub)
	{
		_pri = pri;
		_pro = pro;
		_pub = pub;
	}

	void PrintBase()
	{
		cout << _pri << endl;
		cout << _pro << endl;
		cout << _pub << endl;
	}

private:
	int _pri;
protected:
	int _pro;
public:
	int _pub;
};


// 继承方式--protected
// 基类中public的成员在子类中访问权限已经变成protected
// 基类中protected的成员在子类中访问权限不变
// 基类中private的成员在子类中不可见(不能用---该成员变量确实已经继承到子类中)
class Derived : protected Base
{
public:
	void SetDerived(int priD, int proD, int pubD)
	{
		_priD = priD;
		_proD = proD;
		_pubD = pubD;

		_pro = 10;
		// _pri = 20; // 编译报错,在派生类中不能访问基类中私有的成员变量
	}

	// 访问权限:限定该成员变量是否可以直接在类外进行调用
public:
	int _pubD;
protected:
	int _proD;
private:
	int _priD;
};

class D : public Derived
{
public:
	void Test()
	{
		_pub = 10;
		_pro = 20;
	}
};

int main()
{
	Derived d;
	// d._pub = 10;
	return 0;
}
#endif

#if 0
class Base
{
public:
	void SetBase(int pri, int pro, int pub)
	{
		_pri = pri;
		_pro = pro;
		_pub = pub;
	}

	void PrintBase()
	{
		cout << _pri << endl;
		cout << _pro << endl;
		cout << _pub << endl;
	}

private:
	int _pri;
protected:
	int _pro;
public:
	int _pub;
};


// 继承方式--private
// 基类中public的成员在子类中访问权限已经变成private
// 基类中protected的成员在子类中访问权限已经变成private
// 基类中private的成员在子类中不可见(不能用---该成员变量确实已经继承到子类中)
class Derived : private Base
{
public:
	void SetDerived(int priD, int proD, int pubD)
	{
		_priD = priD;
		_proD = proD;
		_pubD = pubD;

		_pro = 10;
		// _pri = 20; // 编译报错,在派生类中不能访问基类中私有的成员变量
	}

	// 访问权限:限定该成员变量是否可以直接在类外进行调用
public:
	int _pubD;
protected:
	int _proD;
private:
	int _priD;
};

class D : public Derived
{
public:
	void Test()
	{
		// _pub = 10;
		//_pro = 20;
	}
};

int main()
{
	Derived d;
	 d._pub = 10;
	return 0;
}
#endif


#if 0
class Base
{
public:
	void SetBase(int pri, int pro, int pub)
	{
		_pri = pri;
		_pro = pro;
		_pub = pub;
	}

	void PrintBase()
	{
		cout << _pri << endl;
		cout << _pro << endl;
		cout << _pub << endl;
	}

private:
	int _pri;
protected:
	int _pro;
public:
	int _pub;
};

 
// class 和 struct的默认继承权限
// class默认继承权限---priavet
// struct默认继承权限---public
struct Derived : Base
{
public:
	void SetDerived(int priD, int proD, int pubD)
	{
		_priD = priD;
		_proD = proD;
		_pubD = pubD;

		_pro = 10;
		// _pri = 20; // 编译报错,在派生类中不能访问基类中私有的成员变量
	}

public:
	int _pubD;
protected:
	int _proD;
private:
	int _priD;
};

class D : public Derived
{
public:
	void Test()
	{
		_pub = 10;
	}
};

int main()
{
	D d;
	d._pub = 10;
	return 0;
}
#endif

2、基类和派生类对象的赋值转换

赋值兼容规则:
前提---->public继承方式

如果是public继承方式:派生类与基类对象之间是–is-a的关系

is-a: 是一个,可以将一个子类对象看成是一个基类对象

1、所有用到基类对象的位置都可以使用子类对象进行代替
在这里插入图片描述

2、一个基类指针可以指向子类对象
一个子类的指针不能直接指向一个基类的对象。

在这里插入图片描述将B类型指针进行强转 B* Bp -----> (A*) Bp (不建议,存在风险)

此时 Bp 就像 A* Ap 一样,访问基类时不会越界访问。

A* Ap 访问派生类因为其本也只会访问与基类相同的部分。

在这里插入图片描述
3、 基类引用可以引用派生类对象,反之不行 , 因为 引用就是指针。

3、继承中的作用域

同名隐藏:
基类和派生类中具有相同名称的成员(成员变量 || 成员函数)

如果通过派生类对象直接访问同名成员,优先访问到的是派生类自己的

基类的同名成员不能直接访问到(派生类将基类中的同名成员隐藏)


class B
{
public:
	void SetB(int b)
	{
		_b = b;
	}

	void Test(int a)  // 基类
	{}

// protected:
	char _b;
};



// 成员变量:与变量类型是否相同无关
// 成员函数:与成员函数原型是否相同无关
class D : public B
{
public:
	void SetD(int b, int d)
	{
		_b = b;
	}

	void Test()   // 派生类
	{}

//protected:
	int _b;
};

int main()
{
	cout << sizeof(D) << endl;

	D d;
	d._b = '1';
	d.B::_b = '2';

	d.Test(10);
	d.B::Test(10);

	// d.SetD(1); //   报错 , 会先从来自于基类中找到,发现没有; 找来自于基类的必须加上 :: 修饰限定符

	return 0;
}

4、派生类的默认成员函数

1 、如果基类的构造函数是无参或者全缺省的构造函数在派生类构造函

数初始化列表的位置调用或不调用基类构造函数都可以。

如果用户没有调用,则编译器会默认调用。

class B
{
public:
	B() // 基类构造
	{
		cout << "B()" << endl;
	}

protected:
	int _b;
};

class D : public B
{
public:
	D() // 派生构造
	  :B()  //基类含有默认的构造函数 编译器默认给出 ,用来初始化继承自基类的成员,
			  //但必须是此时基类必须是默认的构造函数_d(2)
	{
		cout << "D()" << endl;
	}

protected:
	int _d;
};

int main()
{
	D d;
	return 0;
}

2、如果基类的构造函数带有参数的构造函数(非默认构造),用户必

须在派生类构造函数初始化列表的位置显式调用,以完成基类部分成

员的初始化.


class B
{
public:
	B(int b)
	{
		cout << "B()" << endl;
	}

protected:
	int _b;
};

class D : public B
{
public:
	D()
		: B(1) // 此时必须显示给出,基类不含默认构造函数
		, _d(2)
	{
		cout << "D()" << endl;
	}

protected:
	int _d;
};

int main()
{
	D d;
	return 0;
}


3、如果基类的构造函数带有参数的构造函数,用户必

须在派生类构造函数初始化列表的位置显式调用,以完成基类 部分成

员的初始化。


3、如果基类的构造函数带有参数的构造函数,用户必须在

派生类构造函数初始化列表的位置显式调用,以完成基类 部分成员的初始化

class D : public B
{
public:
	D(int b, int d)
		: B(b)
		, _d(d)
	{
		cout << "D()" << endl;
	}

	D(const D& d)
		: B(d) // 如何理解将派生类对象d调用基类构造呢? 派生类可以看成基类
		, _d(d._d)
	{}

	D& operator=(const D& d)
	{
		if (this != &d)
		{
			//*this = d; 导致死循环
			B::operator=(d);
			_d = d._d;
		}

		return *this;
	}
protected:
	int _d;
};

int main()
{
	D d1(1, 2);
	D d2(d1);

	D d3(3, 4);
	d2 = d3; // 赋值,
	return 0;
}



/*

	如果编译器生成默认的构造函数,则编译器生成的构造函数一定是无参的
	Derived()
	   : Base()
	{}
	
	派生类默认构造函数在其初始化列表位置调用基类构造函数时,必须调用基类
	无参或者全缺省的构造函数

*/




class Base
{
public:
	Base(int b)
		: _b(b)
	{}

	Base(const Base& b)
		: _b(b._b)
	{}

	Base& operator=(const Base& b)
	{
		if (this != &b)
		{
			_b = b._b;
		}

		return *this;
	}

	void SetBase(int b)
	{
		_b = b;
	}

protected:
	int _b;
};

class Derived : public Base
{
public:
	Derived(int b, int d)
		: Base(b)
		, _d(d)
	{}

	Derived(const Derived& d)
		: Base(d)
		, _d(d._d)
	{}

	Derived& operator=(const Derived& d)
	{
		if (this != &d)
		{
			// 给基类部分成员赋值
			Base::operator=(d); 

			// 给派生类新增加成员赋值
			//_d = d._d;//若只对来源于基类进行赋值,则不对派生类进行操作
		}

		return *this;
	}

	void SetDerived(int b, int d)
	{
		_b = b;
		_d = d;
	}

protected:
	int _d;
};


int main()
{
	Base b(10);
	Derived d1(10, 20);
	Derived d2(d1);

	Derived d3(30, 40);
	d2 = d3;
	return 0;
}


在继承体系中,基类与派生类的构造函数与析构函数的调用次序??

	先调用派生类自己的构造函数,再跳转至基类构造函数,
		
class Base
{
public:
	Base(int b)
		: _b(b)
	{
		cout << "Base::Base(int)" << endl;
	}

	~Base()
	{
		cout << "Base::~Base()" << endl;
	}

protected:
	int _b;
};

class Derived : public Base
{
public:
	Derived(int b, int d)
		: Base(b)
		, _d(d)
	{
		cout << "Derived::Derived(int,int)" << endl;
	}

	~Derived()
	{
		cout << "Derived::~Derived()" << endl;
		// call Base::~Base();
	}

protected:
	int _d;
};


// 1. 运行结束打印结果
       /*
	      Base::Base(int)
		  Derived::Derived(int,int)
		  Derived::~Derived()
		  Base::~Base()
	   */
// 2. 构造和析构的调用次序
/*
打印次序(执行次序):
		先调基类构造--->派生类构造--->派生类析构--->基类析构 (从打印结果来看)
	  // 有些情况下,千万不能过于相信自己的眼睛
	  // 打印次序并非调用次序

调用次序:
	  构造次序:
	    派生类构造函数() 
		   : 基类构造函数()  // 打印Base::Base(int)

		{
				 打印Derived(int,int) //派生类构

		}

	  析构次序:
		派生类析构函数() 
		{
		     打印~Derived()// 释放派生类资源  
		  
		   // 编译器在派生类析构函数最后一条有效语句后插了一条汇编代码
		   call 基类析构函数;  //  打印Base::~Base()
		}
*/
void TestDerived()
{
	Derived d(10, 20);
}

int main()
{
	TestDerived();
	return 0;
}
5、继承与友元

友元不能被继承



class Base
{
	friend void Print();
public:
	Base(int b)
		: _b(b)
	{}

	int GetB()
	{
		return _b;
	}
protected:
	int _b;
};

class Derived : public Base
{
public:
	Derived(int b, int d)
		: Base(b)
		, _d(d)
	{}

protected:
	int _d;
};

void Print()
{
	Base b(10);
	cout << b.GetB() << endl;
	cout << b._b << endl;

	Derived d(10, 20);
	cout << d._d << endl;
}		

6、继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成

员。无论派生出多少个子类,都只有一 个static成员实例 。

7、复杂的菱形继承
cout<<"下回讲解"<<endl;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值