封装:
数据的封装,使其只在类中可用,对外部的不可见,防止意外的操作导致的意外的结果。
成员访问限定符:public,private , protected.
public为公有,可以在类外进行访问,private为私有,不能在类外访问,但可以通过一些set和get方法进行访问,protected是受保护的,类似私有但能被派生类访问。
类中对数据进行封装,使其只可以在类内使用,对于一个类,类内的成员有自己独特的意义和限制,按照类内的规则进行操作,如果随意的被类外访问,会导致一些错误。数据封装的越好,则程序的健壮性越强。所以,尽可能的将类内的成员变量都声明为private,而必要的外部访问则通过public的方法来进行操作。
继承:
子类或派生类继承父类或基类,可以直接使用父类的一些方法与属性,大大节省了编程工作量,实现了更高效率的代码复用,实现 模块化,低耦合,高内聚,数据封装。
然而,我自己写的一些代码,至今也没有用过父类以及继承,很多的情况下,对于两个类有相同的类似的操作时,我直接把那段代码ctrl+v到需要的地方了,这样做的原因很简单,我写的都是一些很小的东西,最大的也不过1万行代码,这里的一些代码复用的效率的提升几乎可以忽略,明显ctrl+v的效率更高。而且,按照软件工程课上所说的一个理论,转用新技术所带来的效率的提升并不一定能够弥补学习新技术所带来的消耗。但是这样做也是有后果的,因为很多时候,对于两个类,有类似的方法,但是并不完全相同,而我直接复制之后,经常要花大量的时间去寻找每一个由于为考虑新的类中的一些新的特性导致的bug。所以,类的继承是很有意义的一件事。
属性继承有三种继承方式,public,private,和protected ,默认为private。
public inheritance:
父类的public 和 protected成员在子类中不改变其访问属性。而其private属性则仍然为private,但是为父类的private,子类中不可访问父类中的private成员,即子类中继承了public和protected成员,无父类中的private成员,也无法去访问父类中的private成员。但是父类中的所有成员是存在的,在对象的建立过程中,给了父类中所有成员以地址来保存数据,相当与父类是子类的类中类,但是,子类不能访问父类中的私有成员,但可以同过共有成员去修改或获得私有成员的值。 因为private是为了封装,而如果子类能访问父类的私有成员,那么就无法实现对父类的封装了。
private inheritance;
父类中的public与protected成员在子类中变成了private的成员,但是,private成员依然无法访问。
protected inheritance;
父类中的public和protected成员在子类中都成为了protected成员,private依旧为父类私有。
然后是构造函数:
public:
fa(int aa,int bb,int cc){
a = aa;
b = bb;
c = cc;
}
fa(){
a = 1;
b = 2;
c = 3;
}
int a;
private:
int b;
protected :
int c;
};
class child:public fa{
public:
child(int aa,int bb,int cc,int dd):fa(aa,bb,cc){
d = dd;
}
child(int dd){
}
protected:
int d;
};
对于子类的构造函数,其执行过程是先调用父类的构造函数,然后在使用子类中的构造函数中的内容。所以,对于第二个child(int dd)的构造函数,使用的是fa()的构造函数。
对于有子对象的子类的构造函数
class="cpp">child(int aa,int bb,int cc,int dd,int faa,int fab,int fac):fa(aa,bb,cc),ffa(faa,fab,fac){
d = dd;
}
fa ffa;
这里可以看到,对于派生类所含有的子对象的初始化,以及基类的初始化类似于构造函数的初始化表,分别使用构造函数中的形参调用这些函数对其进行初始化。
析构函数:
析构函数的调用与构造函数的调用顺序刚好相反,先调用派生类中的析构函数,对新增加的新成员进行释放,然后在清理子对象,然后在调用基类的析构函数,对基类中成员进行清理。
多重继承:
当一个类继承两个父类时,这样的多重继承会导致一些二义性问题。若两个父类使用了相同的名称来声明变量时,使用时要声明使用的是哪个父类中的成员。
class="cpp">class child:public fa,public fb{
public:
child(int aa,int bb,int cc,int dd,int faa,int fab,int fac,int fbb):fa(aa,bb,cc),ffa(faa,fab,fac),fb(fbb){
d = dd;
}
fa ffa;
protected:
int d;
};
int main(){
child c = child(1,2,3,4,5,6,7,8);
cout<<c.fb::a<<c.fa::a;
return 0;
}
抽象:
虚基类:
当一个类继承多个类时,若其继承的两个父类拥有相同的基类,且拥有相同作用的基类中的成员时,为了节约空间,应该只用储存一次这些成员,而不是根据这些基类来分配多次且无用的成员。这时,就需要虚基类,当多个类继承同一个基类时,使他们使用虚继承来继承基类,而对于子类,直接继承这些类的基类中的属性,防止二义性。
class="cpp">class F{
public:
int a;
F(int a):a(a){}
};
class FA:virtual public F{
public:
FA(int fa):F(fa){}
};
class FB:virtual public F{
public:
FB(int fb):F(fb){}
};
class C:public FA,public FB{
public:
C(int fa,int fb,int f):FA(fa),FB(fb),F(f){}
};
int main(){
C c = C(1,2,3);
cout<<c.a;
return 0;
}
这里,对于虚基类的构造函数,并不是调用了3次,进行三次初始化,而是只进行一次,而且不是由虚基类的直接派生类进行的初始化,因为派生类的构造函数可能进行了不同效果的初始化,并不相同,所以必须由最后的派生类直接对虚基类进行初始化。虽然虚基类的其他派送类中的构造函数也调用了虚基类的构造函数进行初始化,但是c++编译系统会忽略这些调用。java中没有多重继承。
虚函数:派生类中重新定义与基类中同名的函数,通过基类指针或引用来访问基类和派生类中的同名函数。
class="cpp">class F{
public:
int a;
F(int a):a(a){}
virtual void display(){cout<<a<<endl;}
};
class C:public F{
public:
C(int fa,int f):F(f),fa(fa){}
void display(){cout<<fa<<endl;}
int fa;
};
int main(){
F f1(1);
C c1(2,1);
F* f = &f1 ;
f -> display();
f = &c1;
f -> display();
return 0;
}
如果没写 virtual 时,不能使用基类的指针来调用派送类中的函数,而写了 virtual 声明其为虚函数后,则可以使用基类指针来指向派送类中的方法。对于析构函数类似,当用指向基类的指针指向派生类时,使用 delete 来释放空间时,会调用基类的析构函数,而不是派送类中的析构函数,要正常使用,将析构函数声明虚析构函数。
纯虚函数
=0并不是返回值为0,而是做一个纯虚函数的标志而已。纯虚函数没有函数体,若派生类中没有重新定义纯虚函数,则其仍为纯虚函数。
抽象类:
包含纯虚函数的类即为抽象类。抽象类不能定义对象,只能用来做基类。
多态:
多态性是指,具有不同功能的函数使用相同的函数名,然后就可以使用同一个函数名去实现不同的操作。面向对象中多态性是:向不同的对象发送相同的信息,不同的对象接收后会进行不同的操作或方法。
静态多态性:就是指派送类重写基类中的成员变量和成员函数,然后调用时,虽然名字相同,但效果不同。则 这就是多态。
多态关联:将对象与虚函数结合使用,则实现了运行阶段的多态性。