用了这么久C++,到头来才发现很多基础的东西搞的不够透彻,借此机会巩固一下。
首先看一看C++的三大特性:封装性、继承性、多态性
1.封装性
封装性使得程序更加结构化,并且可以通过访问权限保护数据成员。
C++中结构体和类实际上是同一种东西,在结构体中也可以定义方法,也可以使用权限标识符。两者有两个不同点:
(1)关键字不同,一个是struct,一个是class
(2)成员的默认访问权限不同,结构体的默认访问权限是public,而类的默认访问权限是private
2.继承性
用":"表示继承关系,继承方式有三种,默认为protected继承方式:
(1)public:父类的public、protected权限的成员在子类中的访问权限保持不变
(2)protected:父类的public、protected权限的成员在子类中的访问权限均变为protected
(3)private:父类的public、protected权限的成员在子类中的访问权限均变为private
无论哪种继承方式,父类的private方法都无法被子类访问
3.多态性
在写程序的时候,一个父类会有很多子类,现在如果要写一个通用的函数,参数为该类的对象的指针,那么很显然,我们不希望为每个子类都写一个这样的函数,那样就太麻烦了。能不能只写一个关于父类的函数,而传递参数时传递子类的对象,让该函数自己判断传入对象的类型,然后调用正确的方法呢?答案是可以的,这就是“迟绑定”技术。要实现“迟绑定”,只需要在父类中把需要调用的函数声明为virtual就可以了。
比如下面这个函数:
void fn(Animal *pAn)
{
pAn->eat();
}
Fish是Animal类的一个子类,eat()是Animal中声明的一个虚函数,如果传递Fish对象给函数fn,那么在程序运行时,将会把eat()绑定到Fish对象的eat()方法上,如果Fish类中没有覆盖eat()方法,那么就会绑定到父类Animal的eat()方法。
注意:
(1)如果没有声明为virtual方法,那么fn函数将把Fish对象强制转换为Animal对象,这样调用的就是Animal对象的eat()方法,而不是调用Fish对象的eat()方法了。
(2)可以只在父类中声明函数,而不实现,这样该函数就成为一个纯虚函数:
virtual void eat() = 0;
而父类就变成一个抽象类,不能实例化对象,子类必须实现纯虚方法。
下面再看看C++中的一些其他的重要的特性。
4.函数重载
如果要实现一个通用的加法函数add,在C语言中必须针对不同的数据类型构造不同的函数,如:
addInt(int x, int y);
addFloat(float x, float y);
...
这样就需要记忆很多的函数名,造成了函数接口的不统一。在C++中允许不改变函数名,只改变参数类型,来实现函数的重载,有效的解决了这个问题:
add(int x, int y);
add(float x, float y);
...
重载与返回值无关,函数名相同但返回值不同的两个函数是无法通过编译的。
5.函数的覆盖
这个要和函数重载区分开,函数的覆盖指的是在子类中重写父类中定义的方法。
6.引用与指针
引用就是一个变量的别名,指的是同一块内存区域,本身不占内存空间;而指针存放的是变量的地址,本身要占据4个字节的空间。
引用必须在定义时就赋值,并且以后不能更改所引用的变量;而指针可以存放任意变量的地址,可以随时改变。
在传递函数参数时,有时候引用类型比指针类型看起来更直观,更能表现函数所表达的意思,如:
void swap(int *pA, int *pB)
{
int temp;
temp = *pA;
*pA = *pB;
*pB = temp;
}
调用:swap(&x, &y);//需要传递变量的地址
void swap(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
调用:swap(x, y);//直接传递变量的引用
7.构造函数和析构函数
只需要注意一下父类和子类构造函数和析构函数的调用顺序:
分配内存空间时先调用父类的构造函数,再调用子类的构造函数
释放内存空间时先调用子类的析构函数,再调用父类的析构函数
8.this指针
一个类的所有对象所调用的成员函数都是同一代码段,不同对象调用成员函数时将传递一个隐含的形参:this指针,对所有数据成员的访问都隐式的被加上this->。