封装
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()