后端开发核心技术 第2章面向对象的C++ ③封装继承多态

本文深入探讨了C++中的封装概念,包括私有、公有和受保护成员的访问控制,以及如何通过封装实现接口与实现的分离。同时,详细讲解了C++中的继承与派生,包括派生类的构造函数、析构函数调用顺序,以及多态性的实现。
摘要由CSDN通过智能技术生成

封装

C++中通过实现封装性,把数据和这些有关的操作封装在一个类里面,但是,人们在使用时,往往不关心类的具体实现,只需要知道调用哪个函数会得到什么结果,能实现什么功能
为了实现封装性,提供私有,公有和受保护的3种基本访问权限

(1)私有成员

1)访问权限:只限于类成员访问
2)关键字:private

(2)公有成员

1)访问权限:允许类成员和类外的任何访问
2)关键字:public

(3)受保护成员

1)访问权限:允许类成员和派生类成员访问,不允许类外的任何访问
2)关键字:protect

将接口与实现分离的好处
①如果想修改或者扩充类的功能,只需要修改类中实现,类外部分可以不用修改
②如果发现类中的数据成员数据有错,则只需要在类内检查访问这些数据成员的成员函数

继承与派生

一般形式

class 派生类名:[继承方式] 基类名
{
	派生类新增的成员
}

派生类的访问属性

一、基类的成员函数只能访问基类的成员,不能访问派生类的成员
二、派生类的成员函数可以访问基类的成员
三、在派生类外也可以访问派生类的公有成员,而不能访问派生类的私有成员
在这里插入图片描述

派生类的构造函数

一、派生类的构造函数顺序①基类的构造函数②子对象的构造函数③派生类的构造函数
二、派生类有多个基类,基类调用顺序取决于定义派生类的声明的顺序(从左到右)
三、派生类中不论子对象构造函数取决于在派生类中定义的顺序,与初始化列表的顺序无关
四、如果基类的构造函数定义了一个或者多个参数,派生类必须定义构造函数

#include<iostream>
#include<string>
using namespace std;
class CStudent{//声明基类Student
public:
   CStudent(int n,string nam,char s){//基类构造函数
      num=n;
      name=nam;
      sex=s;
   }
   ~CStudent(){} //基类析构函数
protected: //保护部分
   int num;
   string name;
   char sex ;
};
class CStudent1: public CStudent{//声明派生类Student1
public : //派生类的公用部分
   CStudent1 (int n,string nam,char s,int a,string ad): CStudent (n,nam,s){//派生类构造函数
      age=a; //在函数体中只对派生类新增的数据成员初始化
      addr=ad;
   }
   void show(){
      cout<<"num: "<<num<<endl;
      cout<<"name: "<<name<<endl;
      cout<<"sex: "<<sex<<endl;
      cout<<"age: "<<age<<endl;
      cout<<"address: "<<addr<<endl<<endl;
   }
   ~CStudent1(){ } //派生类析构函数
private : //派生类的私有部分
   int age;
   string addr;
};
int main(){
   CStudent1 stud1(10010,"Wang-li",'f',19,"115 Beijing Road,Shanghai");
   CStudent1 stud2(10011,"Zhang-fun",'m',21,"213 Shanghai Road,Beijing");
   stud1.show(); //输出第一个学生的数据
   stud2.show(); //输出第二个学生的数据
   return 0;
}

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

一、基类构造函数
二、成员对象构造函数
三、派生类构造函数
四、派生类的析构函数
五、成员类对象的析构函数
六、基类的析构函数

#include<iostream>
using namespace std;
class CBase{
public:
    CBase (){ std::cout<<"CBase::CBase()"<<std::endl; }
    ~ CBase (){ std::cout<<"CBase::~CBase()"<<std::endl; }
};

class CBase1:public CBase {
public:
    CBase1 (){ std::cout<<"CBase::Base1()"<<std::endl; }
    ~ CBase1 (){ std::cout<<"CBase::~Base1()"<<std::endl; }
};

class CDerive{
public:
    CDerive (){ std::cout<<"CDerive::CDerive()"<<std::endl; }
    ~ CDerive (){ std::cout<<"CDerive::~CDerive()"<<std::endl; }
};

class CDerive1:public CBase1{
private:
    CDerive m_derive;
public:
    CDerive1(){ std::cout<<"CDerive1::CDerive1()"<<std::endl; }
    ~CDerive1(){ std::cout<<"CDerive1::~CDerive1()"<<std::endl; }
};

int main(){
    CDerive1 derive;
    return 0;
}

运行结果
CBase::CBase()
CBase::Base1()
CDerive::CDerive()
CDerive1::CDerive1()
CDerive1::~CDerive1()
CDerive::~CDerive()
CBase::~Base1()
CBase::~CBase()

多态

多态

一个接口,多种实现,用一个函数名调用不同内容的函数
使用基类指针访问派生类中的同名函数

#include <iostream>
#include <string>
using namespace std;
/*声明基类Box*/
class Box{
public:
   Box(int,int,int);  //声明构造函数
   virtual void display();//声明输出函数
protected:  //受保护成员,派生类可以访问
   int length,height,width;
};
/*Box类成员函数的实现*/
Box:: Box (int l,int h,int w){//定义构造函数
   length =l;
   height =h;
   width =w;
}
void Box::display(){//定义输出函数  
   cout<<"length:" << length <<endl;
   cout<<"height:" << height <<endl;
   cout<<"width:" << width <<endl;
}
/*声明公用派生类FilledBox*/
class FilledBox : public Box{
public:
   FilledBox (int, int, int, int, string);//声明构造函数
   virtual void display();//虚函数
private:
   int weight;//重量
   string fruit;//装着的水果
};
/* FilledBox类成员函数的实现*/
void FilledBox :: display(){//定义输出函数
   cout<<"length:"<< length <<endl;
   cout<<"height:"<< height <<endl;
   cout<<"width:"<< width <<endl;
   cout<<"weight:"<< weight <<endl;
   cout<<"fruit:"<< fruit <<endl;
}
FilledBox:: FilledBox (int l, int h, int w, int we, string f ) : Box(l,h,w), weight(we), fruit(f){}
int main(){//主函数
   Box box(1,2,3);//定义Student类对象stud1
   FilledBox fbox(2,3,4,5,"apple");//定义FilledBox类对象fbox
   Box *pt = &box;//定义指向基类对象的指针变量pt
   pt->display( );
   pt = &fbox;
   pt->display( );
   return 0;
}
运行结果
length:1
height:2
width:3
length:2
height:3
width:4
weight:5
fruit:apple

虚函数机制

虚函数表和虚函数指针

虚函数使用方法

一、在基类中用virtual关键字声明成员函数为虚函数
二、在派生类中重新定义此函数,要求函数名、类型、参数个数全部与基类的虚函数相同
三、使用基类指针指向派生类对象

纯虚函数

加 “= 0”;

virtual void function() = 0

动物可以作为基类派生出老虎、孔雀等子类,但是动物本生生成对象不合常理。为了解决上述问题,引入纯虚函数。
任何含纯虚函数的类被成为抽象类,不能生成对象,任何试图对该类进行实例化的语句都是错误的

class Animail{
public:
    virtual void GetColor()  = 0;
};
class Dog : public Animail{
public:
     virtual void GetColor() {cout <<"Yellow"endl;};
};
class Pig : public Animail{
public:
 virtual void GetColor() {cout <<"White"<<endl;};
};
int main(){
     Animail cAnimail;//出错,因为GetColor没有定义,无法生成对象
     return 0;
}

析构函数声明为虚函数

构造函数不能被声明未虚函数,因为编译器在构造对象时,必须知道确切类型,才能正确的生成对象,其次,在构造函数执行之前,对象并不存在,无法使用指向此对象的指针来调用构造函数
析构函数不是虚函数容易引发内存泄漏当派生类对象有基类指针删除,如果基类带有一个非虚函数的析构函数,会导致派生类的成分没有被销毁掉
例子

#include<iostream>
using namespace std;
class Base{
public:
    Base(){ std::cout<<"Base::Base()"<<std::endl; }
    ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
};
class Derive:public Base{
public:
    Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
    ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
};
int main(){
    Base* pBase = new Derive(); 
    /*这种base classed的设计目的是为了用来"通过base class接口处理derived class对象"*/
    delete pBase;
    return 0;
}
输出
Base::Base()
Derive::Derive()
Base::~Base()

如果将~Base()修改为virtual ~Base(),则可以正常析构
输出为
Base::Base()
Derive::Derive()
Base::~Base()
Derive::~Derive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值