类和对象的基本概念
1.结构化程序设计
C语言使用结构化程序设计:
程序=数据结构+算法
(1)程序由全局变量以及众多相互调用的函数组成。
(2)算法以函数的形式实现,用于对数据结构进行操作。
结构化程序设计模式:
结构化程序设
结构化程序设计的不足:
(1)结构化程序设计中,函数和其所操作的数据结构,没有直观的联系。
(2)随着程序规模的增加,程序逐渐难以理解,很难一下子看出来:
①某个数据结构到底有哪些函数可以对它进行操作?
②某个函数到底是用来操作哪些数据结构的?
③任何两个函数之间存在怎样的调用关系?
④结构化程序设计没有“封装”和“隐藏”的概念。要访问某个数据结构中的某个变量,就可以直接访问,那么当该变量的定义有改动的时候,就要把所有访问该变量的语句找出来修改,十分不利于程序的维护、扩充。
⑤难以查错,当某个数据结构的值不正确时,难以找出到底是那个函数导致的。
⑥重用:在编写某个程序时,发现其需要的某项功能,在现有的某个程序里已经有了相同或类似的实现,那么自然希望能够将那部分代码抽取出来,在新程序中使用。
⑦在结构化程序设计中,随着程序规模的增大,由于程序大量函数、变量之间的关系错综复杂,要抽取这部分代码,会变得十分困难。
总之,结构化的程序,在规模庞大时,会变得难以理解,难以扩充(增加新功能),难以查错,难以重用。
面向对象的程序设计
软件业的目标是更快、更正确、更经济地建立软件。。如何更高效地实现函数的复用?如何更清晰的实现变量和函数的关系?使得程序更清晰更易干修改和维护。
(1)面向对象的程序设计方法,能够较好解决上述问题。
面向对象的程序=类+类+...+类
(2)设计程序的过程,就是设计类的过程
(3)面向对象的程序设计方法:
①将某类客观事物共同特点(属性)归纳出来,形成一个数据结构(可以用多个变量描述事物的属性);
②将这类事物所能进行的行为也归纳出来,形成一个个函数,这些函数可以用来操作数据结构(这一步叫“抽象”)。
(4)然后,通过某种语法形式,将数据结构和操作该数据结构的函数“捆绑”在一起,形成一个“类”,从而使得数据结构和该数据结构的算法呈现出显而易见的紧密关系,这就是“封装。”
面向对象的程序设计具有“抽象”,“封装“, ”继承”, “多态” 四个基本特点。
二.类和对象
从客观事物抽象出类
写一个程序,输入矩形的长和宽,输出面积和周长。
比如对于“矩形”这种东西,要用一个类来表示,该如何做“抽象”呢?
矩形的属性就是长和宽。因此需要两个变量,分别代表长和宽。
一个矩形,可以有哪些行为呢(或可以对矩形进行哪些操作)?
1.矩形可以有设置长和宽,算面积,和算周长这三种行为(当然也可以有其他行为)。
2.这三种行为,可以各用一个函数来实现,他们都需要用到长和宽这两个变量。
3.将长、宽变量和设置长,宽,求面积,以及求周长的三个函数“封装”在一起,就能形成一
个“矩形类”。
4.长、宽变量成为该“矩形类”的“成员变量”,三个函数成为该类的“成员函数”。成员
变量和成员函数统称为类的成员。
实际上,“类”看上去就像“带函数的结构”。
//写一个程序,输入矩形的长和宽,输出面积和周长。
#include <iostream>
using namespace std;
int main()
{
int c, k;
int zc, mj;
cout << "请输入长和宽:" << endl;
cin >> c>> k; //输入长和宽
zc = 2*c + 2*k;
mj = c * k;
cout << "长为" << c << "宽为" << k << "的周长=" << zc << endl;
cout << "长为" << c << "宽为" << k << "的面积=" << mj << endl;
return 0;
}
使用函数:(从客观事物抽象出类)
//写一个程序,输入矩形的长和宽,输出面积和周长。
#include <iostream>
using namespace std;
class CRectangle //创建一个CRectangle类
{
public: //
int w, h; //定义宽和高
//Area(), Perimeter(), Init为成员函数
int Area()
{
return w * h;
}
int Perimeter()
{
return 2 * (w + h);
}
void Init(int w_, int h_) //初始化
{
w = w_; h = h_; //设置宽和高
}
}; //必须要有逗号
int main()
{
int w, h;
CRectangle r; //r是一个对象
cin >> w >> h;
r.Init(w, h); //r.Init作用r在成员函数上面 //调用Init上CRectangle类的成员函数
cout << r.Area() << endl << r.Perimeter() << endl; //输出Area()和Perimeter
return 0;
}
(1)通过类,可以定义变量。类定义出来的变量,也称为类的实例,就是我们所说的“对象”
(2)C++中,类的名字就是用户自定义的类型的名字。可以像使用基本类型那样来使用它。CRectangle 就是一种用户自定义的类型。
对象的内存分配
(1)和结构变量一样,对象所占用的内存空间的大小,等于所有成员变量的大小之和。对于上面的CRectangle类,sizeof(CRectangle)=8
(2)每个对象各有自己的存储空间。一个对象的某个成员变量被改变了,不会影响到另一个对象。
对象间的运算
和结构变量一样,对象之间可以用进行赋值,但是不能用“==”,“!=”,“>”, “<”,“>=”,“<=”进行比较,除非这些运算符经过了“重载”。
使用类的成员变量和成员函数
用法1:对象名.成员名
CRectangle r1,r2;
r1.w = 5;
r2.Init(5,4);
(1)lnit函数作用在 r2 上,即Init函数执行期间访问的w和h是属于r2 这个对象的,执行r2.lnit 不会影响到r1。
用法2.指针->成员名
CRectangle r1,r2;
CRectangle * p1 = &r1;
CRectangle * p2 = &r2;
p1->w = 5; // Init作用在p2指向的对象上
p2->Init (5,4) ;
用法3:引用名.成员名
CRectangle r2 ;
CRectangle &rr = r2; //引用了,rr和r2是同一个
rr.w =5;
rr.init (5,4) ; //rr的值变了,r2的值也变
void PrintRectangle (CRectangle & r) //CRectangle 作为参数
{
cout <<r.Area() <<","<<r.Perimeter(); //输出Area()和Perimeter
}
CRectangle r3; //初始化r3对象
r3.Init(5,4);
PrintRectangle (r3);
类的成员函数和类的定义分开写
class CRectangle
{
public: //公有成员,可以在任何地方访问
int w,h;
int Area(); //成员函数仅在此处声明
int Perimeter(); //成员函数仅在此处声明
void init(int w_,int h_);
};
成员函数体在外面写:
int CRectangle::Area() //成员函数:Area()普通名字+CRectangle类的名字。
{
return w*h;
}
int CRectangle::Perimeter()
{
return 2*(w+h);
}
void CRectangle::Init( int w_,int h_)
{
w=w_; h=h_;
}
//成员函数:Area()普通名字+CRectangle类的名字。
//::(两个冒号)代表Area不是一个全局的函数,它是一个类的成员函数,属于一个CRectangle的成员函数。
CRectangle::说明后面的函数是CRectangle类的成员函数,而非普通函数。那么,一定要通过对象或对象的指针或对象的引用才能调用。
类成员的可访问范围
1.在类的定义中,用下列访问范围关键字来说明类成员可被访问的范围:
private: 私有成员,只能在成员函数内访问
public: 公有成员,可以在任何地方访问
protected: 保护成员,以后再说
2.以上三种关键字出现的次数和先后次序都没有限制。
定义一个类
class className
{
private: //说明类成员的可访问范围
私有属性和函数
public:
公有属性和函数
protected:
保护属性和函数
};
3.如过某个成员前面没有上述关键字,则缺省地被认为是私有成员。
class Man
{
int nAge; //私有成员
char szName[20]; //私有成员
public:
void setName(char * szName)
{
strcpy( Man :: szName,szName);
}
};
4.在类的成员函数内部,能够访问:
(1)当前对象的全部属性、函数;
(2)一同类其它对象的全部属性、函数。
5.在类的成员函数以外的地方,只能够访问该类对象的公有成员。
#include <iostream>
using namespace std;
class CEmployee //创建一个员工类
{
public: //私有成员
char szName[30]; //名字
public : //公有成员
int salary; //工资
void setName(const char * name); //设置姓名
void getName(const char * name); //获取姓名
void averageSalary(CEmployee e1, CEmployee e2); //平均工资
};
void CEmployee::setName(const char * name) //设置员工的姓名
{
strcpy(szName, name); //可以访问类的任何成员变量和成员函数
}
void CEmployee::getName(const char * name)
{
strcpy(szName, name); //可以访问类的任何成员变量和成员函数
}
void CEmployee::averageSalary(CEmployee e1, CEmployee e2)
//不是成员函数作用当前对象,它是同类的其它对象
{
cout << e1.szName; //ok,访问同类其他对象私有成员
salary = (e1.salary + e2.salary) / 2;
}
int main()
{
CEmployee e;
strcpy(e.szName,"Tom123456789"); //编译错误,不能访问私有成员
/*
strcpy(e.setName, "Tom123456789"); //编译错误
一个类的私有成员不可以在这个类的创元函数外部进行访问
*/
e.setName("Tom");
e.salary=5000;
return 0;
}
(1)设置私有成员的机制,叫“隐藏”
(2)“隐藏”的目的是强制对成员变量的访问一定要通过成员函数进行,那么以后成员变量的类型等属性修改后,只需要更改成员函数即可。否则,所有直接访问成员变量的语句都需要修改。
“隐藏”的作用
(1)如果将上面的程序移植到内存空间紧张的手持设备上,希望将szName改为char szName[5],若szName不是私有,那么就要找出所有类似
strcpy(e.szName, "Tom1234567889");
这样的语句进行修改,以防止数组越界。这样做很麻烦。
(2)如果将szName变为私有,那么程序中就不可能出现(除非在类的内部)
strcpy (e.szName, "Tom1234567889");
这样的语句,所有对szName的访问都是通过成员函数来进行,比如:
e.setName("Tom12345678909887");
(3)那么,就算szName改短了,上面的语句也不需要找出来修改.只要改setName成员函数,在里面确保不越界就可以了。
成员函数的重载及参数缺省
成员函数也可以重载
成员函数可以带缺省参数。
#include <iostream>
using namespace std;
class Location
{
private:
int x;
int y;
public:
void init(int x = 0, int y = 0);
void valuex(int val)
{
x = val;
}
int valueX()
{
return x;
}
};
void Location::init(int X, int Y)
{
x = X;
y = Y;
}
int main()
{
Location A, B;
A.init(5);
A.valueX(5);
cout << A.valueX();
return 0;
}
使用缺省参数要注意避免有函数重载时的二义
class Location
{
private :
int x,y;
public:
void init( int x =0, i;
int y= o );
void valuex( int val = 0)
{
x = val;
}
int valuex()
{
return x;
}
};
Location A;
A.valueX()//错误,编译器无法判断调用哪个valuex