c++学习笔记 继承和派生

c++的三大特性:封装、继承、多态。

继承的目的:提高代码重用、提高开发效率。

B继承于A类,A派生B,A为基类,B为派生类。

继承得到父类的数据和方法,自己新添一定的数据和方法。

父类是共性,子类是个性。

class 父类{};

class 子类 :继承方式 父类名

{

//新增子类的数据。

};

继承三种方式:私有、保护、公共。

private/protected/public(推荐)


这三种继承方式相同点是:三种继承方式都不可以访问父类的私有数据。

不同点是:公共继承,不改变父类其他数据和方法的类型,比如父类是公共的子类也是公共的,父类是保护的子类也是保护的。

保护继承:无论父类是何种类型的数据和方法,继承后都是保护的。

私有继承:无论·父类是何种类型的数据和方法,继承后都是私有的。

注意:这样的变化是编译器自己完成的,不需要人为进行操作。

#include<iostream>
using namespace std;
class Base
{
	private:
		int a;
		protected:
		int b;
		public:
			int c;
			Base(){
			}
			Base( int a,int b,int c):a(a),b(b),c(c){}	
	
};

class Son: public Base
{
	public:
		void func()
		{
			cout<<b<<c<<endl;
		}
	Son(int a,int b,int c):a(a),b(b),c(c)
	{
		
	}
};
/*
Base::Base( int a, int b, int c)
{
	this->a=a;
	this->b=b;
	this->c=c;
}*/
int main()
{
	Base ob1( 1,2,3);
	cout<<ob1.c<<endl;
	Son ob(1,2,3);
	ob.func();
	return 0;
}

 子类构造析构的顺序:

构造顺序: 父类->成员->子类。

析构顺序: 子类->成员->父类。

#include<iostream>
using namespace std ;
 class Base
{
 public:
 Base()
 {
 	cout<<"父类构造"<<endl; 
	 }	
	 ~Base()
	 {
	 	cout<<"父类析构"<<endl;
	 }
};
class Other
{
	public:
		Other()
		{
			cout<<" Other构造"<<endl; 
		}
		~Other()
		{
			cout<<" Other析构"<<endl; 
		}
};
class Son : public Base
{
	public:
		Other ob;
			public:
		Son()
		{
			cout<<" Son构造"<<endl; 
		}
		~Son()
		{
			cout<<" Son析构"<<endl; 
		}
};
int main()
{
	Son ob;
	return 0;
}

按照这个构造的顺序,我们为了使父类构造和成员构造,在此之前发生。

子类想调用·父类的有参构造和成员的有参构造,必须用到初始化列表。

值得注意的是如果我们想调用父类的1参构造我们需要用父类的类名称,而调用成员的有参构造我们用写成员名。

#include<iostream>
using namespace std ;
 class Base
{
	private:
		int a;
 public:
 Base()
 {
 	cout<<"父类构造"<<endl; 
 	 
	 }	
	  Base( int a)
	  {
	  	cout<<"父类有参构造"<<endl;
		  this->a=a; 
	  }
	 ~Base()
	 {
	 	cout<<"父类析构"<<endl;
	 }
};
class Other
{
	private:
		int b;
	public:
		Other()
		{
			cout<<" Other构造"<<endl; 
			
		}
		Other(int b)
		{
			this->b=b;
			cout<<"Other有参构造"<<endl;
			 
		}
		~Other()
		{
			cout<<" Other析构"<<endl; 
		}
};
class Son : public Base
{
	public:
		Other ob;
		int c;
			public:
		Son()
		{
			cout<<" Son构造"<<endl; 
		}
		Son( int a,int b,int c):Base(a),ob(b)//初始化列表 
		{
			  this->c=c;
			  cout<<" Son有参构造"<<endl; 
		}
		~Son()
		{
			cout<<" Son析构"<<endl; 
		}
};
int main()
{
	Son ob(1,2,3);
	return 0;
}

2,在继承的过程中,子类和父类同名现象是很普遍的,也是我们在继承的过程中需要注意和解决的问题。

同名成员,最简单最安全的方式是加作用域。

访问父类,加父类的做用域,访问子类不用。

子类会优先访问自己的成员。

#include<iostream>
using namespace std;
class Base 
{
   public:
   int a;
   public:
 Base()
 {
 //	cout<<"父类构造"<<endl; 
 	 
	 }	
	  Base( int a)
	  {
	 // 	cout<<"父类有参构造"<<endl;
		  this->a=a; 
	  }
	 ~Base()
	 {
	 //	cout<<"父类析构"<<endl;
	 }	
};
class  Son :   public  Base
{
    public:
	int a ;
	Son(int x,int y):Base(x)
	{
		a=y;
	}
};
int main()
{
	Son ob(1,20);
	cout<<ob.a<<endl;
	cout<<ob.Base::a<<endl;
	return 0;
}

 从上面的结果我们看出当我们需要访问父类的成员时,我们需要加作用域。

成员方法也是这样的。

当我们子类重载了函数名,子类不能访问父类相同函数名的方法,即使他们参数不一样。

这时我们需要加入作用域。

总的来说继承过程中的同名处理,就是加作用域。

3,多继承

我们可以从一个类继承,亦可以同时的从多个类继承,这就是多继承。但是多继承备受争议,从多个类的继承会导致函数变量等同名造成的歧义。

这时我们最简单的方法还是加作用域。

3.2 菱形继承

菱形继承:有公共祖先的继承叫菱形继承。

最底层的子类 数据会包含多份(公共祖先的数据)

举一个例子,动物是父类,之下有两个子类 羊和驼,他们又同时有一个相同的子类,羊驼。

这时羊驼有两份祖先动物的数据,这就是菱形继承。当我们访问这个数据时,会产生二义性,这个时候我们一需要加作用域。

3.3虚继承

虚继承是解决菱形继承的多份数据的问题,虚继承就是在继承中加 virtual 修饰

class  子类 : virtual  继承方式 父类

{

};

还是上面的例子,如果羊和驼都是虚继承动物,羊驼继承羊和驼,这时我们只会得到一份数据不会产生二义性。

虚继承会产生一个虚基类指针,指向虚基类表,表里记录了这个数据的偏移量对应的是空间数据起始地址偏移。

在最有羊驼继承的过程中,羊驼会继承两个虚基类指针,而两个指针的数据偏移量是不一样的但指向的都是一份数据。

注意;虚继承是为了解决公共祖先引发的多义性,但是对于非公共祖先产生的多义性无法解决。

多继承在工程开发中是弊大于利的,对于代码维护也是非常困难的,在设计方法上多继承可以用单继承来代替。
 

                        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​感谢阅读!!!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值