您的位置:首页 > 编程语言 > C语言/C++

浅析C++之封装、继承、多态

2017-01-09 16:28 267 查看
面向对象编程的三个基本特征:封装、继承和多态。

一、封装

1、什么是封装?

封装即隐藏对象的属性和实现细节,仅对外公开接口。在C++中就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。

2、封装的功能

隐藏细节,使代码模块化。

3、类的成员访问权限

一个类的成员(属性和函数),有以下3种可能的访问权限:

public:所有人均可访问。
protected:只有本类和本类的派生类中的成员函数可以访问。
private:只有本类的成员函数可以访问。

二、继承

1、什么是继承?

继承是指可以让某个类型的对象获得另一个类型的对象的功能(属性和函数)的方法。

通过继承创建的新类称为“子类”或“派生类”。

被继承的类称为“父类”或“基类”或“超类”。

2、继承的功能

代码重用。使子类可以拥有父类的属性和方法,也可以定义自己特有的属性和方法,或重新定义父类的方法。

3、实现方式

实现继承:直接使用基类的属性和方法而无需额外编码的能力;
接口继承:仅使用属性和方法的名称、但是子类必须提供实现的能力;

4、继承权限

无论哪种权限,父类中private的成员都对子类不可见。

public:父类中的public和protected的成员在子类中仍为public和protected。
Protected:父类中public和protected的成员在子类中都变成了protected权限。
Private:父类中public和protected的成员在子类中都变成了private权限。

5、例

#include<iostream>

using namespace std;

class A{
public:
int x;
void sety(int Y) { y = Y;}
protected:
int y;
private:
int z;
};

class B: public A{
public:
void test(){
cout<<x<<endl
<<y<<endl;
//cout<<z<<endl;  //编译错误,子类B不能访问父类A的私有成员c
}
};

int main(){
B b;
b.x = 1;
// b.y = 2;  //int b对于class B来说是protected权限,只能由本类或本类的派生类访问,不能被直接赋值
b.sety(2);
b.test();
return 0;
}


运行结果:



三、多态

1、什么是多态?

多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。

2、多态的功能

派生类的功能可被基类的方法或引用变量所调用,提高了可扩展性。

3、多态的三个必要条件

要有继承体系结构
要有方法的重写(继承中的一些类必须具有和基类中的虚函数同名的成员函数)
父类引用指向子类对象(对于父类中定义的方法,如果子类重写了该方法,那么指向子类对象的父类类型的引用将会调用子类中的该方法)

4、需要了解的几个概念

4.1 虚函数

       定义:在基类中声明为virtual并在一个或多个派生类中被重新定义的成员函数。(基类中虚函数必须实现,否则编译器报错)

       格式:virtual ReturnType FunctionName(Parameter)method
body
};


       作用:实现多态的方式之一。通过虚函数,在调用不同的派生类的时候,可以拥有不同的功能。

       意义:虚函数的意义,就在于定义了一个从最早的父类,到最后的子类,都必须具备的一个功能(函数),只是在不断的进化(继承)中,这个功能会略微发生改变。通过虚函数,我们在调用不同的派生类的时候,可以拥有不同的功能。然后我会说:这么麻烦,干脆每个继承类都重写命名一个函数算了,只要知道重命名的函数有这个功能就行了不是?理论上来说,完全可以,在一个父类和其继承类不多的项目中,这么做完全可以,只要你自己能熟记或者找到这个重命名函数是干嘛用的;但是在大一点的项目中,由于类中的函数成百上千,恐怕你就会为此疯狂。

4.2 纯虚函数

      定义:在基类中只有声明,没有实现的虚函数,但要求任何派生类都要定义自己的实现方法。
      格式:virtual ReturnType Function()= 0;

4.3 抽象类

      定义:包含纯虚函数的类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。在C++中把只能用于被继承而不能直接创建对象的类设置为抽象类(Abstract Class)。

4.4 重载overload

     定义:函数名相同,参数列表不同。重载只是在类的内部存在。但是不能靠返回类型来判断。

4.5 重写override

     定义:也叫做覆盖。子类重新定义父类中有相同名称和参数的虚函数。函数特征相同。但是具体实现不同,主要是在继承关系中出现的 。

     重写需要注意:

被重写的函数不能是static的。必须是virtual的。
重写函数必须有相同的类型,名称和参数列表
重写函数的访问修饰符可以不同。尽管virtual是private的,派生类中重写改写为public,protected也是可以的

4.5 重定义redefining

     定义:也叫做隐藏。子类重新定义父类中有相同名称的非虚函数 ( 参数列表可以不同 ) 。

     如果一个类,存在和父类相同的函数,那么,这个类将会覆盖其父类的方法,除非你在调用的时候,强制转换为父类类型,否则试图对子类和父类做类似重载的调用是不能成功的。

5、例

#include<iostream>
using namespace std;

class Person{
public:
virtual void sayHi() { cout<<"Just hi"<<endl; }
};

class Mary: public Person{
public:
virtual void sayHi() { cout<<"hi, I'm Mary"<<endl; }//重写虚函数
};

class Nancy: public Person{
public:
virtual void sayHi() { cout<<"hi, I'm Nancy"<<endl; }//重写虚函数
};

int main(){
int which = 1;

Person * p;
while(which >= 1 && which <= 3){
cout<<"1 == Person, 2 == Mary, 3 == Nancy"<<endl;
cin>>which;

switch(which){
case 1: p = new Person; break;
case 2: p = new Mary; break;
case 3: p = new Nancy; break;
}
if(which >= 1 && which <= 3){
p->sayHi();
delete p;
}
}
return 0;
}


运行结果:



———————————————————————————————————————————————————————————————————
最后,测试一下自己理解的怎么样吧,你能正确写出下面的小程序的运行结果吗?
#include<iostream>
#include<string>
using namespace std;
//father class
class A{
public:
virtual void display();
//virtual static void display();    //声明错误,因为static修饰的函数编译时要求前期绑定,而虚函数是动态绑定???什么是动态绑定?
virtual void say() = 0;    //纯虚函数,需要在派生类中实现

void exec();

void f1(string a);    //overload,重载。两个f1函数在class A的内部被重载。(函数名相同,参数类型不同)
void f1(int a);
};
void A::display(){
cout<<"baseA dispaly"<<endl;
}

void A::exec(){
display();
say();
}

void A::f1(string a){
cout<<"baseA f1(string a)"<<endl;
}

void A::f1(int a){
cout<<"baseA f1(int a)"<<endl;
}

//sub class B
class B: public A{ //不是public class A
public:
void display();//override,重写/覆盖基类中的display虚函数
void say();//对基类中纯虚函数的实现

void f1(int a, int b);//重定义,即隐藏
void f1(int a);
};

void B::display(){
cout<<"deriveB display"<<endl;
}

void B::say(){
cout<<"deriveB say"<<endl;
}

void B::f1(int a, int b){
cout<<"deriveB f1(int a, int b)"<<endl;
}

void B::f1(int a){
cout<<"deriveB f1(int a)"<<endl;
}
//sub class C
class C: public A{
public:
//void display();    //在class C中不重写class A中的display()
void say();//对基类中纯虚函数的实现,抽象类的每一个派生类都需要对纯虚函数进行实现
};

void C::say(){
cout<<"deriveC say"<<endl;
}

int main(){
B b;    //创建B的实例
C c;
// A a;  //包含纯虚函数的class A为抽象类,不能实例化,否则编译出错
A *a = &b;    //A类指针指向B类对象。 A含有纯虚函数,为抽象类,不能创建实例。
A *a1 = &c;
/*虚函数*/  /*重写,即覆盖*/
a->display();   //class B重写了display(),调用输出为B display
a1->display();    //class C中未重写display(),调用基类的display()
cout<<"*****"<<endl;
/*纯虚函数*/
a->say();   //输出为B say
b.say();    //输出为B say
cout<<"*****"<<endl;
/**/
a->exec();    //输出为B display B say   原因:B类继承了A类的exec函数。

/*重载*/
string str = "abc";
int int1 = 1;
a->f1(str);    //a为基类类型,指向派生类B的对象
a->f1(int1);   //
cout<<"*****"<<endl;
/*重定义*/
int int2 = 2;
//a->f1(int1, int2);    //错误调用。f1(int a, int b)是class B特有的重定义的函数,a是基类类型的,不能调用子类特有的方法
b.f1(int1,int2);    //派生类B的对象调用重定义的f1函数。
b.f1(int1);    //对于不是虚函数的普通函数,派生类重定义了该函数。派生类调用的是其重新定义的f1函数
a->f1(int1);    //而父类指针虽然指向子类对象,但调用的是父类定义的该函数。
//b.f1(str);    //对f1进行了重定义的派生类B不可以再调用基类的f1函数,否则编译错误。重定义的函数覆盖了父类的方法。
c.f1(str);    //未对f1进行重定义的派生类C可以调用基类的f1函数。

return 0;
}


运行结果:



参考:
http://blog.csdn.net/huangyimo/article/details/50480313

http://blog.csdn.net/songzitea/article/details/40659327
http://blog.csdn.net/songzitea/article/details/40659327 http://www.cnblogs.com/weizhixiang/articles/5760286.html http://www.cnblogs.com/dongsheng/p/3343939.html http://blog.chinaunix.net/uid-26851094-id-3327323.html http://blog.csdn.net/sinat_20265495/article/details/50112311 http://www.cnblogs.com/biyeymyhjob/archive/2012/07/16/2594079.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  继承 多态 c++ 封装 重载