C++之继承篇

本文详细介绍了C++中的继承权限,包括protected和private的区别。接着讨论了继承的定义格式,同名隐藏的概念,以及在对象模型中如何表现。特别地,文章通过测试代码展示了基类和派生类的对象模型,强调了派生类对象的构造顺序。此外,还提及了菱形继承和虚继承在解决二义性问题中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.继承权限

继承权限与访问权限
 public(公有访问)protected(保护访问)private(私有访问)
public(公有继承)继承后变成public(公有访问)继承后变成protected(保护访问)继承后不可访问
protected(保护继承)继承后变成protected(保护访问)继承后变成protected(保护访问)继承后不可访问
private(私有继承)继承后变成private(私有访问)继承后变成private(私有访问)继承后不可访问

注:protected和private的区别:

                                         protected:在派生类中可以访问,类外不可访问

                                          private:派生类和类外均不可访问


2.继承定义格式

    

3.同名隐藏

  定义:基类,派生类某一个成员变量同名,或者某一个成员函数同名(只要函数名一样,不管参数列表,返回值是否一样),通过派生类调用该成员变量或成员函数时,只会调用派生类的,基类的看不到,这种现象称为同名隐藏。

#include <iostream>
#include <windows.h>
using namespace std;


class Base
{

public:
	int _pub;
	 void Fun1()
	{
		cout<<"Base::void Fun1()"<<endl;
	}
	 void Fun2(int)
	{
		cout<<"Base::void Fun2(int)"<<endl;
	}
	int Fun3()
	{
		cout<<"Base::int Fun3()"<<endl;
		return 0;
	}
};



class Derived:public Base
{

public:
	int _pub;
	 void Fun1()
	{
		cout<<"Derived::Fun"<<endl;
	}
	 void Fun2()//与Base中Fun2相比不带参数
	{
		cout<<"Derived::void Fun2(int)"<<endl;
	}
	void Fun3()//与Base中Fun3相比返回值不同
	{
		cout<<"Derived::void Fun3()"<<endl;
		
	}
};
int main()
{
  
  Derived d;
  Base b;
  b._pub=1;
  d._pub=2;
  cout<<d._pub<<endl;
  d.Fun1();
  d.Fun2();
  d.Fun3();
  
  system("pause");
  return 0;
}

结果:


4.对象模型



测试代码:

#include <iostream>
#include <windows.h>
using namespace std;

class Base
{

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



class Derived:public Base
{

public:
	int _pub1;
	
};
int main()
{
  
  Derived d;
  Base b;
  b._pub=1;
  b._pro=2;
  b._pri=3;

  d._pub1=4;
  d._pub=5;
  d._pro=6;
  d._pri=7;
  system("pause");
  return 0;
}


总结:基类的对象模型是按照声明次序的。派生类是先是基类的,然后是自己的(按照声明顺序排列)。

5.派生类对象

class Base
{

public:
	Base()
	{
		cout<<"Base::Base()"<<endl;
	}
	~Base()
	{
		cout<<"Base::~Base()"<<endl;
	}
private:
	int _pub;
	int _pro;
	int _pri;
	
};



class Derived:public Base
{

public:
   Derived()
   {
	   cout<<"Derived::Derived()"<<endl;
   }
    ~Derived()
   {
	   cout<<"Derived::~Derived()"<<endl;
   }
private:
	int _pub1;
	
};
定义一个派生类对象
Derived  d;
d的调用顺序为:Derived()->~Derived()
  实际执行顺序:Base() ->Derived()-> ~Derived()->~Base()
小结:
   (1)基类有缺省构造函数,派生类无构造函数,系统会给派生类自动合成
   (2)基类无构造函数,派生类无构造函数,系统不会给基类,派生类自动合成
     (3)基类构造函数有参数,派生类必须定义构造函数,并在初始化列表显示调用基类的构造函数
6.赋值兼容规则-----public继承
   
#include <iostream>
#include <windows.h>
using namespace std;

class Base
{

public:
	int _b;
	
};



class Derived:public Base
{

public:
   int _d;
	
};
int main()
{
  Base b;
  Derived d;

  b=d;
  d=b;

  Base &b1=d;
  Base *p=&d;

  Derived &d=b;
  Derived * p1=&b;
  }




小结:
   (1)子类对象可以赋值给父类对象。原理就像是切片/切割一样。
  (2)父类对象不能赋值给子类对象。
  (3)父类的指针/引用可以指向子类对象。
  (4)子类的指针/引用不能指向父类对象。
7.友元函数&Static&继承
 友元函数属于类外函数,所以不能继承。
 static修饰的成员可以继承,但是,无论多少派生类,只有一份Static修饰的成员。
8.多继承
如: 有class A,class B, 
     class C:public A,public B
   C类的父类有两个,便成C类为多继承。
注:继承多个类时,每个类的继承权限不能省略,否则,class默认的继承权限是private。
9.菱形继承

#include <iostream>
#include <windows.h>
using namespace std;

class B
{
public:
	int _b;
};

class C1:public B
{
public:
	int _c1;
};
class C2:public B
{
public:
	int _c2;
};
class D:public C1,public C2
{
public:
	int _d;
};
int main()
{
  D d;
  d.C1::_b;
  d._c1;
  d.C2::_b;
  d._c2;
  d._d ;
  system("pause");
  return 0;
}



注:D类对象访问_b时,需要加上作用域,不然会产生二义性。因为C1,C2类各有一个_b,D类对象访问时,不知要访问哪个。

10.虚继承---解决菱形继承的二义性和数据冗余的问题

继承权限前面加上virtual关键字。

例如:

class A
{
public:
	int _a;
};
class B:virtual public A
{
public:
	int _b;
};

int main()
{
	B b;
	b._a=1;
	b._b=2;
 return 0;
}


小结:

(1)虚继承中,在程序编译时,偏移量的表格已经生成。在调用构造函数时,只是将地址存入。

(2)虚继承中,A类若是无构造函数,B类自动合成构造函数。(将指向偏移量的地址存入对象的前四个字节)。

(3)虚继承与普通继承的区别在于,调用构造函数前,先PUSH 1,以此来判断是否为虚继承,若是,便执行(2)中所说。

(4)虚继承中,基类的成员变量只存在一份,所以当派生类对象调用基类对象时,不会存在二义性问题,因此,虚继承可以解决菱形继承中的二义性问题和数据冗余的问题。


用虚继承解决菱形继承中二义性问题:

#include <iostream>
#include <windows.h>
using namespace std;

class B
{
public:
	int _b;
};

class C1:virtual public B
{
public:
	int _c1;
};
class C2:virtual public B
{
public:
	int _c2;
};
class D:public C1,public C2
{
public:
	int _d;
};
int main()
{
  D d;
  d._b=1;
  d._c1=2;
  d._c2=3;
  d._d =4;
  system("pause");
  return 0;
}




注意: 是C1和C2虚继承B类,所以只在这前面加virtual关键词。

下面举一个在不同位置加VIRTUAL关键词,看会引起什么不同

#include <iostream>
#include <windows.h>
using namespace std;

class B
{
public:
	int _b;
};

class C1: public B
{
public:
	int _c1;
};
class C2: public B
{
public:
	int _c2;
};
class D:virtual public C1,virtual public C2
{
public:
	int _d;
};
int main()
{
  D d;
  d.C1::_b=1;
  d._c1=2;
  d.C2::_b=5;
  d._c2=3;
  d._d =4;
  system("pause");
  return 0;
}


上述的这种情况,并没有解决菱形二义性的问题。





                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值