C++类继承

1、类继承(派生)

使用继承的场景:
1)如果新创建的类与现有的类相似,只是多出若干成员变量或成员函数时,可以使用继承
2)当需要创建多个类时,如果它们拥有很多相似的成员变量或成员函数,可以将这些类共同的成员提取出来,定义为基类,然后从基类继承

class father{
}
class son:public father{  //public 继承方式
}
  • 类成员的访问权限由高到低依次为: public -> protected -> private,public成员在类外可以访问,private成员只能在类的成员函数中访问。
  • 如果不考虑继承关系,protected成员和private成员一样,类外不能访问。但是,当存在继承关系时,protected和private就不一样了。基类中的protected成员可以在派生类中访问,而基类中的private成员不能在派生类中访问
  • 继承方式有三种: public(公有的)、protected(受保护的)和private(私有的)。一般就选择public
- 在派生类中,可以通过基类的公有成员函数间接访问基类的私有成员 - 使用 `using`关键字可以改变基类public、protected成员在派生类中的权限
class A{
public:
	int m_a=10;
protected:
	int m_b=20;
privated:
	int m_c=30;
}
class B:public A{
public:
	using A::m_b;
privated:
	using A::m_a;
}

2、类的对象模型

1)创建派生类对象时,先调用基类的构造函数,再调用派生类的构造函数
2)销毁派生类对象时,先调用派生类的析构函数,再调用基类的析构函数
3)创建派生类对象时只会申请一次内存(大小是基类+派生类占用的内存空间之和),派生类对象包含了基类对象的内存空间,this 指针相同,成员变量地址也相同
4) 创建派生类对象时,先初始化基类对象,再初始化派生类对象。
创建派生类时,申请一块基类+派生类的内存空间,先调用基类的构造函数初始化基类的成员,再调用派生类的构造函数初始化派生类的成员
5)C++中,不同继承方式的访问权限只是语法上处理(派生类无法访问基类的私有成员,但实际通过直接操作内存的方式是可以访问的,比如通过指针)

//基类A 派生类B
B *P=new B;
*((int*)P+2)=30;//修改私有成员
delete P;

3、如何构造基类

class A{
public:
	int m_a;
private:
	int m_b;
punlic:
	A():m_a(0),m_b(0); //初始化列表
	{
		cout<<"调用了基类的默认构造函数\n";
	}
	A(int a,int b):m_a(a),m_b(b); //两个参数的构造函数
	{
		cout<<"调用了基类的构造函数A(int a,int b)\n";
	}
	A(const A&a):m_a(a.m_a+1),m_b(a.m_b+1); //基类的拷贝构造函数
	{
		cout<<"调用了基类的拷贝构造函数\n";
	}
	void showA(){count<<"m_a="<<m_a<<"m_b"<<m_b<<endl;}
}
class B:public A
{
public:
	int m_c;
	B():m_c(0),A()   //派生类的默认构造函数
	{
		cout<<"调用派生类的构造函数\n";
	}
	B(int a,int b,int c):A(a,b),m_c(c)   //指明派生类的2参数的构造函数
	{
		cout<<"调用派生类的构造函数\n";
	}
	B(const A&a,int c):A(a),m_c(c)   //指明派生类的拷贝构造函数
		{
		cout<<"调用派生类的构造函数\n";
	}
	void showB(){count<<"m_c="<<m_c<<endl;}
}
int main()
{
	B b1;
	
	B b2(1,2,3);
	
	A a(10,20);
	B b3(a,30);
	
}

原则:基类的成员由基类的构造函数初始化,派生类新增的成员由派生类初始化

拷贝构造函数:用一个存在的对象创建新的对象,不会调用普通的构造函数,而是调用拷贝构造函数。
如果类中没有定义拷贝构造函数,编译器将提供一个拷贝构造函数,将已存在对象的成员变量值赋给
新对象的成员变量。
语法:类名 新对象名(存在的对象名)     类名 新对象名=存在的对象名

拷贝构造函数的语法:类名(const 类名&对象名, 其他参数){.........}
注意:
1、拷贝构造函数权限为public 
2、函数名与类名相同  
3、没有返回值不写void      
4、如果以值传递的方式调用函数,函数的实参为对象,会调用拷贝构造函数

多态

基类指针只能调用基类的成员函数,不能调用派生类的成员函数。如果在基类的成员函数前加 virtual 关键字,把它声明为虚函数,基类指针就可以调用派生类中同名的成员函数,通过派生类中同名的成员函数,就可以访问派生对象的成员变量。
有了虚函数,基类指针指向基类对象时就使用基类的成员函数和数据,指向派生类对象时就使用派生类的成员函数和数据,基类指针表现出了多种形式,这种现象称为多态。 基类引用也可以使用多态。
注意:
1)只需要在基类的函数声明中加上 virtual 关键字,函数定义时不能加。。
2)在派生类中重定义虚函数时,函数特征要相同。
3)当在基类中定义了虚函数时,如果派生类没有重定义该函数,那么将使用基类的虚函数。
4)在派生类中重定义了虚函数的情况下,如果想使用基类的函数,可以加类名和域解析符。指针名->基类名::函数名
5)如果要在派生类中重新定义基类的函数,则将它设置为虚函数;否则,不要设置为虚函数,有两方面的好处:首先效率更高;其次,指出不要重新定义该函数。

多态应用场景

  • 基类的虚函数实现基本功能。
  • 派生类重定义虚函数,扩展功能、提升性能。
    虽然继承也可以实现这种功能,但使用多态可以让编程更简单方便
//根据不同英雄释放不同技能
class hero{
public:
	virtual void skill1(){}
	virtual void skill2(){}
	virtual void skill3(){}
}
class XS:public Hero{
	void skill1(){"XS";}
	void skill2(){"XS";}
	void skill3(){"XS";}
}
class LB:public Hero{
	void skill1(){"LB";}
	void skill2(){"LB";}
	void skill3(){"LB";}
}
class HX:public Hero{
	void skill1(){"HX";}
	void skill2(){"HX";}
	void skill3(){"HX";}
}
int main()
{
	Hero*ptr=nullptr;
	if(id==1){
		ptr=new XS;
	}
	if(id==2){
		ptr=new LB;
	}
	if(id==3){
		ptr=new HX;
	}
	if(prt!=nullptr){
		prt->skill1();
		prt->skill2();
		prt->skill3();
		delte ptr;
	}
}

如果基类没有声明虚函数,内存模型里只有定义的静态变量;如果声明了虚函数,多了一个vfptr虚函数指针,指向vftable虚函数列表,在列表中,存放了基类不同虚函数的地址。派生类和基类内存模型相同,在创建派生类对象后,在虚函数列表中,会用派生类成员函数取代基类成员函数的地址。如果派生类没有重写虚函数,那么在派生类的虚函数列表中还是基类函数的地址。

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值