文章目录
一、面向对象的思想
- 目的:实现软件设计的产业化
- 观点:自然界是由实体(对象)所组成。
- 程序设计方法:使用面向对象的观点来描述模仿并处理现实问题。
- 要求:高度概括、分类和抽象。
二、OOP的基本特点
- 抽象
抽象是对具体对象(问题)进行概括,抽出这一类对象的公共性质并加以描述的过程。
数据抽象:描述某类对象的属性或状态。int Hour,int Minute,int Second
代码抽象:描述某类对象的共有的行为特征或具有的功能。SetTime(),ShowTime()
抽象的实现:通过类的声明。
举例:
class cClock
{
public:
void SetTime(int NewH,int NewM,int NewS);
void ShowTime();
private:
int Hour,Minute,Second;
};
-
封装
将抽象出的数据成员、代码成员相结合,将它们视为一个整体。
实现封装:类声明中的{ }
目的是为了增强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员。 -
继承和派生
-
多态性
多态:统一名称,不同功能的实现方式。
目的:达到行为标识符的统一,减少程序中标识符的个数。
实现:重载函数和虚函数。
三、类的概念和声明
1. 类是具有相同属性和行为的一组对象的集合。
2. 类是一种用户自定义类型,声明形式:
class 类名称
{
public:
公有成员(外部接口)
private:
私有成员
protected:
保护型成员
}
- 公有类型成员:在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
- 私有类型成员:在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。 (如果紧跟在类名称后面声明私有成员,则关键字private可以省略)
- 保护类型成员:与private类似,其差别表现在继承与派生时对派生类的影响不同。
四、对象
-
类的对象时该类的某一特定实体,即类类型的变量。
-
声明形式:
类名 对象名;
例如: Clock myClock; -
类中成员的访问方式
类中成员互访——直接使用成员名
类外访问——使用“对象名.成员名”方式访问public属性成员。
五、构造函数
- 构造函数的作用:在对象被创建时使用特定的值构造对象,或者说将对象初始化为一个特定的状态。
- 在对象创建时由系统自动调用。
- 如果程序中未声明,则系统自动产生出一个默认形式的构造函数。
- 允许为内联函数、重载函数、带默认形参值的函数。
class Clock
{
public:
Clock (int NewH, int NewM, int NewS);//构造函数
void SetTime(int NewH, int NewM, int NewS);
void ShowTime();
private:
int Hour,Minute,Second;
};
//构造函数的实现:
Clock::Clock(int NewH, int NewM, int NewS)
{
Hour= NewH;
Minute= NewM;
Second= NewS;
}
//建立对象时构造函数的作用:
void main()
{
Clock c (0,0,0); //隐含调用构造函数,将初始值作为实参。
c.ShowTime();
}
六、析构函数
- 完成对象被删除前的一些清理工作。
- 在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间。
- 如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数。
七、内联成员函数
在类中声明内联成员函数的方式:
- 将函数体放在类的声明中
- 使用inline关键字
八、拷贝构造函数
- 拷贝构造函数是一种特殊的构造函数,其形参为本类对象的引用。
class 类名
{ public :
类名(形参);//构造函数
类名(类名 &对象名);//拷贝构造函数
...
};
类名:: 类名(类名 &对象名)//拷贝构造函数的实现
{ 函数体 }
-
重写拷贝构造函数
Char *pname1,*pname2;
Pname1=“hello”;
Pname2=pname1;(如果是类中数据成员有指针,这样是×)
Pname2=“hello”;(重写拷贝构造函数)
普通的构造函数直接等起来,两个指针指向同一个内存,如果pname1先释放,pname2怎么办。所以当类中数据成员有指针时候,要重写拷贝构造函数。 -
拷贝构造函数举例
#include "stdafx.h"
#include <iostream>
using namespace std;
class Point //Point 类的声明
{
public: //外部接口
Point(int xx = 0, int yy = 0) { X = xx; Y = yy; } //构造函数
Point(Point &p); //拷贝构造函数
int GetX() { return X; }
int GetY() { return Y; }
private: //私有数据
int X, Y;
};
//成员函数的实现
Point::Point(Point &p)
{
X = p.X;
Y = p.Y;
cout << "拷贝构造函数被调用" << endl;
}
//形参为Point类对象的函数
void fun1(Point p)
{
cout << p.GetX() << endl;
}
//返回值为Point类对象的函数
Point fun2()
{
Point A(1, 2);
return A;
}
//主程序
void main()
{
Point A(4, 5); //第一个对象A
Point B(A); //情况一,用A初始化B。第一次调用拷贝构造函数
cout << B.GetX() << endl;
fun1(B); //情况二,对象B作为fun1的实参。第二次调用拷贝构造函数
B = fun2(); //情况三,函数的返回值是类对象,函数返回时,调用拷贝构造函数
cout << B.GetX() << endl;
}
运行结果:
拷贝构造函数被调用
4
拷贝构造函数被调用
4
拷贝构造函数被调用
1
拷贝构造函数被调用的三种情况
- 当用类的一个对象去初始化该类的另一个对象时。
void main(void)
{ Point A(1,2);
Point B(A); //拷贝构造函数被调用
cout<<B.GetX()<<endl;
}
- 若函数的形参为类对象,调用函数时,实参赋值给形参。
void fun1(Point p)
{ cout<<p.GetX()<<endl;
}
void main()
{ Point A(1,2);
fun1(A); //调用拷贝构造函数
}
- 当函数的返回值是类对象时。
Point fun2()
{ Point A(1,2);
return A; //调用拷贝构造函数
}
void main()
{
Point B;
B=fun2();
}
A是局部变量,离开建立他的函数就消亡了,不可能在主函数继续生存,编译系统会在主函数中创建一个无名临时对象,该临时对象的生存期只在函数调用所处的表达式中,也就是B=fun2();
九、类的组合
- 类中的成员数据是另一个类的对象。
- 可以在已有的抽象的基础上实现更复杂的抽象。
- 原则:不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化。
- 声明形式:
类名::类名(对象成员所需的形参,本类成员形参):对象1(参数),对象2(参数),…
{ 本类初始化 } - 构造函数调用顺序:先调用内嵌对象的构造函数(按内嵌时的声明顺序,先声明者先构造)(注意,内嵌对象在构造函数初始化列表中出现的顺序与内嵌对象构造函数的调用顺序无关。)。然后调用本类的构造函数。(析构函数的调用顺序相反)
- 若调用默认构造函数(即无形参的),则内嵌对象的初始化也将调用相应的默认构造函数。
- 举例
#include "stdafx.h"
#include <iostream>
#include <cmath>
using namespace std;
class Point //Point类声明
{
public:
Point(int xx = 0, int yy = 0) { m_iX = xx; m_iY = yy; } //内联构造函数
Point(Point &p); //拷贝构造函数
int GetX() { return m_iX; }
int GetY() { return m_iY; }
private:
int m_iX, m_iY;
};
//拷贝构造函数的实现
Point::Point(Point &p)
{
m_iX = p.m_iX;
m_iY = p.m_iY;
cout << "Point拷贝构造函数被调用" << endl;
}
//类的组合
class Line //Line类的声明
{
public: //外部接口
Line(Point xp1, Point xp2); //构造函数
Line(Line &); //拷贝构造函数
double GetLen() { return len; }
private: //私有数据成员
Point p1, p2; //Point类的对象p1,p2
double len;
};
//组合类的构造函数
Line::Line(Point xp1, Point xp2)
:p1(xp1), p2(xp2)
{
cout << "Line构造函数被调用" << endl;
double x = double(p1.GetX() - p2.GetX());
double y = double(p1.GetY() - p2.GetY());
len = sqrt(x*x + y*y);
}
//组合类的拷贝构造函数
Line::Line(Line &Seg) : p1(Seg.p1), p2(Seg.p2)
{
cout << "Line拷贝构造函数被调用" << endl;
len = Seg.len;
}
//主函数
void main()
{
Point myp1(1, 1), myp2(4, 5); //建立Point类的对象
Line line(myp1, myp2); //建立Line类的对象
Line line2(line); //利用拷贝构造函数建立一个新对象
cout << "The length of the line is:";
cout << line.GetLen() << endl;
cout << "The length of the line2 is:";
cout << line2.GetLen() << endl;
}
运行结果:
Point拷贝构造函数被调用
Point拷贝构造函数被调用
Point拷贝构造函数被调用
Point拷贝构造函数被调用
Line构造函数被调用
Point拷贝构造函数被调用
Point拷贝构造函数被调用
Line拷贝构造函数被调用
The length of the line is:5
The length of the line2 is:5
- 前向引用声明
1、类应该先声明,后使用。
2、如果需要在某个类的声明之前,引用该类,则应进行前向引用声明。
class B; //前向引用声明
class A
{ public:
void f(B b);
};
class B
{ public:
void g(A a);
};
//这边是放在函数的形参的,函数形参只有函数被调用时才起作用。此时还没分配内存空间,函数的形参只有被调用时才分配内存空间。
3、需要注意,尽管使用了前向引用声明,但是在提供一个完整的类的声明之前,不能声明该类的对象,也不能在内联成员函数中,使用该类的对象。
class Fred; //前向引用声明
class Barney {
Fred x; //错误:类Fred的声明尚不完善
};
class Fred {
Barney y;
};
class Fred; //前向引用声明
class Barney {
public:
void method()
{
x->yabbaDabbaDo(); //错误:Fred类的对象在定义之前被使用
}
private:
Fred* x; //正确,经过前向引用声明,可以声明Fred类的对象指针
//只涉及到指针的第一层含义,指针都是四个字节,存储地址。此时和Fred的完整定义还没有关系。
};
class Fred {
public:
void yabbaDabbaDo();
private:
Barney* y;
};
4、应该记住:当你使用前向引用声明时,你只能使用被声明的符号,而不能涉及类的任何细节。
十、UML简介
-
UML语言是一种可视化的面向对象的建模语言。
-
UML有三个基本的部分:事物(Things)、关系(Relationships)、图(Diagrams)。
-
事物:结构事物、动作事物、分组事物、注释事物。
-
关系:依赖、关联、泛化、实现。
-
图:类图、对象图、用例图、顺序图、协作图、状态图、活动图、组件图、实施图。