一、面向对象程序设计的四个基本特点
1. 抽象
面向对象方法中的抽象,是指对具体问题(对象)进行概括,抽出 一类对象 的公共性质并加以描述的过程。
对一个问题的抽象应该包括两个方面:数据抽象和行为抽象(或称为功能抽象、代码抽象)。前者描述某类对象的属性或状态,也就是此类对象区别于彼类对象的特征;后者描述的是某类对象的共同行为或功能特征。
例子:
时钟
数据抽象:int hour, int minute, int second;
行为抽象:showTime(),setTime();
对人进行抽象
共同的属性,数据抽象:string name, string sex, int age;
共同的行为,行为抽象:eat(), walk(), work(), study();
2. 封装
封装就是将 抽象得到的 数据和行为(或功能)相结合,形成一个有机的整体,也就是将 数据 与 操作数据的函数代码 进行有机的结合,形成“类”,其中的数据和函数都是类的成员。
例如:时钟类的定义如下:
class Clock //class 关键字 类名
{ //边界
public: //外部接口void setTime(int newH, int newM, int newS);//行为,代码成员void showTime(); //行为,代码成员
private: //特定的访问权限int hour, minute, second; //属性,数据成员
}; //边界
这里定义了一个名为Clock的类,其中的函数成员和数据成员,描述了抽象的结果。
声明为 public 的两个函数为类提供了外部接口,外界只能通这个接口来与 Clock 类发生联系;
声明为 private 的3个整型数据是本类的私有数据,外部无法直接访问。
3. 继承
C++语言中提供了类的继承机制,允许程序员在保持原有类特性的基础上,进行更具体、更详细的说明。通过类的这种层次结构,可以很好地反映出特殊概念与一般概念的关系。
4. 多态
多态性是指一段程序 能够处理多种类型对象 的能力。在C++语言中,这种多态性可以通过强制多态、重载多态、类型参数化多态、包含多态4种形式来实现。
- 强制多态是通过将一种类型的数据转换成另一种类型的数据来实现的,也就是前面介绍过的数据类型转换(隐式或显式)。
- 重载是指给同一个名字赋予不同的含义,如上篇的函数重载。
这两种多态属于特殊多态性,只是表面的多态性。
- C++中采用虚函数实现包含多态。虚函数是多态性的精华。
- 模板是C++实现参数化多态性的工具,分为函数模板和类模板两种。
这两种多态属于一般多态性,是真正的多态性。
二、类
类的定义
定义类的语法形式如下:
class 类名称 { public:外部接口 protected:保护型成员 private:私有成员 };
类成员的访问控制:
访问控制属性可以有以下3种:
公有类型(public)、私有类型(private)和保护类型(protected)
- 公有类型成员定义了类的外部接口,在类外只能访问类的公有成员;
- 私有成员只能被本类的成员函数访问,来自类外部的任何访问都是非法的;
- 保护类型成员的性质和私有成员的性质相似,其差别在于继承过程中对产生的新类影响不同。
例子
计算大圆减小圆面积
#include <iostream>
//#include "func.h"
using namespace std;class Cirle
{
public:void setRadius(double r){radius = r;}double area(){return 3.1415*radius*radius;}
private:double radius;
};int main()
{Cirle c1;c1.setRadius(10);Cirle c2;c2.setRadius(5);double ret;ret = c1.area()-c2.area();cout << ret << endl;cout << "Hello World!" << endl;return 0;
}
带参: Clock a(1,2,3);
class Clock
{
public:Clock(int h = 0,int m = 0, int s = 0);void setTime(int h, int m, int s){hour = h;minute = m;second = s;}void showTime(){cout << hour << ":" << minute << ":" << second << endl;}
private:int hour;int minute;int second;
};Clock::Clock(int h,int m,int s)
{hour = h;minute = m;second = s;
}int main()
{Clock a(1,2,3);a.showTime();cout << "Hello World!" << endl;return 0;
}
无参/用默认参数:调用时无括号-->Clock a;
int main()
{Clock a;a.showTime();cout << "Hello World!" << endl;return 0;
}
初始化列表
初始化列表只能出现在构造函数中;
class Clock
{
public:Clock(int h = 0,int m = 0, int s = 0);void setTime(int h, int m, int s){hour = h;minute = m;second = s;}void showTime(){cout << hour << ":" << minute << ":" << second << endl;}
private:int hour;int minute;int second;
};Clock::Clock(int h,int m,int s): hour(h), minute(m), second(s)
{}
虚数例子:
my_complex.h
#ifndef MY_COMPLEX_H
#define MY_COMPLEX_H
class Complex
{
public:Complex(double r = 0,double i = 0);void show();void setReal(double r);void setImag(double i);private:double real;double imag;
};
#endif // MY_COMPLEX_H
my_complex.cpp
#include "my_complex.h"
#include <iostream>
using namespace std;
void Complex::show()
{cout << real << "+" << imag << "i" << endl;
}void Complex::setReal(double r)
{real = r;
}
void Complex::setImag(double i)
{imag = i;
}Complex::Complex(double r, double i) : real(r), imag(i)
{}
main.cpp
#include <iostream>
using namespace std;
#include "my_complex.h"
int main()
{Complex c1;c1.setReal(1);c1.setImag(2);c1.show();Complex c2(1.1,2.2);c2.show();cout << "Hello World!" << endl;return 0;
}
三、构造函数与析构函数
构造函数
构造函数的作用就是在对象被创建时利用特定的值构造对象,将对象初始化为一个特定的状态。构造函数主要目的是为了初始化。
特性
- 构造函数的函数名与类名相同,而且没有返回值;
- 构造函数通常被声明为公有函数;
- 只要类中有了构造函数,编译器就会 在建立新对象的地方 自动插入 对构造函数调用的代码。因此我们通常说构造函数 在对象被创建的时候 将被自动调用。
如果类中没有写构造函数,编译器会自动生成一个隐含的默认构造函数,该构造函数的参数列表和函数体皆为空。
重载的构造函数
class Clock {
public:Clock(int newH,int newM, int newS);//构造函数Clock() //构造函数{ hour=0;minute=0;second=0;void setTime(int newH, int newM, int newS);void showTime();
private:int hour, minute, second;
};
//其他函数实现略
int main()
{Clock c1(0,0,0); //调用有参数的构造函数Clock c2; //调用无参数的构造函数...
}
这里的构造函数有两种重载形式:有参数的和无参数的(即默认构造函数)。
复制构造/拷贝构造
复制构造函数是一种特殊的构造函数,具有一般构造函数的所有特性,其形参是本类
的对象的引用(&)。其作用是使用一个已经存在的对象(由复制构造函数的参数指定),去初
始化同类的一个新对象。
复制构造函数的调用:
析构函数
析构函数与构造函数的作用几乎正好相反,它用来完成对象被删除前的一些清理工作,也就是专门做扫尾工作的。析构函数是在对象的生存期即将结束的时刻被自动调用的。它的调用完成之后,对象也就消失了,相应的内存空间也被释放。
特性
- 析构函数的名称是由类名前面加“~"构成,没有返回值;
- 析构函数不接收任何参数,但可以是虚函数;
- 构造次数 == 析构次数;
- 构造次数 > 析构次数 说明内存泄露。
- 析构函数是构造函数的逆顺序。
四、类的组合
类的组合描述的就是 一个类 内嵌其他类的对象 作为成员的情况,它们之间的关系是一种包含与被包含的关系。
当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动
创建。因为部件对象是复杂对象的一部分,因此,在创建对象时既要对本类的基本类型数
据成员进行初始化,又要对内嵌对象成员进行初始化。(创建:先内嵌,后本类)
例子:
#include <iostream>using namespace std;
class Point //point 类定义
{
public:Point(int x = 0,int y = 0); //构造函数Point(const Point &other); void setX(int x);void setY(int y);int getX(){return m_x;}int getY(){return m_y;}void show();~Point(){cout << "~Point"<< endl;}
private:int m_x,m_y;
};void Point ::show()
{cout << "(" << m_x << "," << m_y << ")" << endl;
}
Point ::Point(int x, int y):m_x(x),m_y(y)
{cout <<" p(int,int) "<<endl;
}
//复制构造函数的实现
Point ::Point(const Point &other):m_x(other.m_x),m_y(other.m_y)
{cout << "point &other " << endl;
}//类的组合
class Line //line类的定义
{
public: //外部接口Line(int x1,int y1,int x2,int y2):m_p1(x1,y1),m_p2(x2,y2){cout << "line(int,int,int,int)" <<endl;}Line(const Point &p1,const Point &p2):m_p1(p1),m_p2(p2){cout<<"point &p"<<endl;}void show(){m_p1.show();cout<<"->";m_p2.show();cout<<endl;}~Line(){cout<<"~Line"<<endl;}
private: Point m_p1,m_p2; //point类的对象m_p1,m_p2
};
int main()
{Point p1(3,4); Point p2(9,10); //建立point类对象Line l(p1,p2); //建立line类的对象l.show();return 0;
}