c++学习笔记第四章 面向对象
数据抽象。
钟表类实例:
class Clock
{
public:
void SetTime(int NewH,int NewM,int NewS);
void ShowTime();
private:
int Hour,Minute,Second;
}
封装:将抽象出的数据成员,代码成员相结合,将他们视为一个整体。
通过类中的{}来实现封装。
上文中的:SetTime,ShowTime是外部接口。
继承和派生,多态性(同一个名称,不同功能的实现方式,目的是达到行为标识统一,减少程序标识符的个数,实现是重载函数和虚函数)
类包括属性和行为两个部分。
class 类名称
{
public:
公有成员(外部接口)
private:
私有成员
protected:
保护型成员
}
public后面声明类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
private后面只允许本类的函数访问。
void Clock::SetTime(int NewH,int NewM,int NewS)
{
Hour=NewH;
Minute=NewM;
Second=NewS;
}
void Clock::ShowTime()
{
cout<<Hour<<":"<<Minute<<":"<<Second;
}
内联成员函数:将函数体放在类的声明中的一种方式。
内联成员函数举例(一)
class Point
{
public:
void Init(int initX,int initY)
{
X=initX;
Y=initY;
}
int GetX() {return X;}
int GetY() {return Y;}
private:
int X,Y;
};
内联成员函数举例(二)
class Point
{
public:
void Init(int initX,int initY);
int GetX();
int GetY();
private:
int X,Y;
};
inline void Point:: Init(int initX,int initY)
{
X=initX;
Y=initY;
}
inline int Point::GetX()
{
return X;
}
inline int Point::GetY()
{
return Y;
}
成员和类的默认访问修饰符是 private。
对象:
类的对象是该类的某一特定实体,即该类类型的变量。
声明:
类名 对象名;
Clock myclock;
类中成员的访问方式:
类内:直接使用成员名;
类外:使用对象名.成员名方式访问public属性的成员。
构造函数:
1.构造函数的作用是在对象呗创建时使用特定的值构造对象,将对象初始化为一个特定的状态。
2.在对象创建时,由系统自动调用。
3.如果在程序中未声明,则由系统自动产生出一个默认形式的构造函数。
4.允许为内联函数,重载函数,带默认形参值的函数。
实例:
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();
}
拷贝构造函数:拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。
类比:
int a(0);
int b(a);
拷贝构造函数是一种特殊的构造函数,形参为本类的对象引用。
定义:
class 类名
{ public :
类名(形参);//构造函数
类名(类名 &对象名);//拷贝构造函数
...
};
类名:: 类名(类名 &对象名)//拷贝构造函数的实现
{ 函数体 }
class 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;
}
拷贝构造函数不太会啊。。。。
如果程序员没有为类声明拷贝初始化构造函数,则编译器自己生成一个拷贝构造函数。
这个构造函数执行的功能是:用作为初始值的对象的每个数据成员的值,初始化将要建立的对象的对应数据成员。
(按bit位复制)
析构函数
完成对象被清除前的一些清理工作。
在对象的生存期结束的时刻系统自动的调用它,然后再释放对象所属的空间。
如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数。
#include<iostream>
using namespace std;
class Point
{
public:
Point(int xx,int yy);
~Point();
//...其它函数原形
private:
int X,int Y;
};
Point::Point(int xx,int yy)
{ X=xx; Y=yy;
}
Point::~Point()
{
}
//...其它函数的实现略
class A
{
public:
A(int m, int n)
{ v = m;
p = new int(n);}
~A() { delete p; }//析构函数
private:
int v;
int *p;
};
类的应用举例(例4-3)
一圆型游泳池如图所示,现在需在其周围建一圆型过道,并在其四周围上栅栏。栅栏价格为35元/米,过道造价为20元/平方米。过道宽度为3米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。
#include<iostream>
using namespace std;
const float PI = 3.14159;
const float FrencePrice = 35;//栏杆造价
const float ConcreatePrice =20;//过道造价
//声明Circle及其数据和方法
class Circle
{
private:
float radius;
public:
Circle(float r);//构造函数
float Circumference();//求周长
float Area();//求面积
};
//类的实现
//构造函数初始化数据成员radius
Circle ::Circle(float r)
{radius=r;}
//计算圆的周长
float Circle::Circumference()
{
return 2*PI*radius;
}
//计算圆的面积
float Circle::Area()
{
return PI*radius*radius;
}
void main()
{
float radius;
float FenceCost,ConcreteCost;
//输入半径
cin>>radius;
//声明Circle对象
Circle Pool(radius);
Circle PoolRim(radius+3);
//计算栅栏造价
FrenceCost=PoolRim.Circumference()*FrencePrice;
cout<<FenceCost<<endl;
//计算过道造价
ConcreteCost=(PoolRim.Area()-Pool.Area())*ConcretePrice;
cout<<ConcreteCost<<endl;
}
组合的概念
类中的成员数据是另一个类的对象。
可以在已有的抽象基础上实现更加复杂的抽象
class Point
{ private:
float x,y; //点的坐标
public:
Point(float h,float v); //构造函数
float GetX(void); //取X坐标
float GetY(void); //取Y坐标
void Draw(void); //在(x,y)处画点
};
//...函数的实现略
class Line
{
private:
Point p1,p2; //线段的两个端点
public:
Line(Point a,Point b); //构造函数
Void Draw(void); //画出线段
};
//...函数的实现略
原则:不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化。
声明形式(初始化列表):
类名::类名(对象成员所需的形参,本类成员形参)
:对象1(参数),对象2(参数),......
{ 本类初始化 }
构造函数调用顺序:先调用内嵌对象的构造函数(按内嵌时的声明顺序,先声明者先构造)。然后调用本类的构造函数。(析构函数的调用顺序相反)
若调用默认构造函数(即无形参的),则内嵌对象的初始化也将调用相应的默认构造函数。
class Part //部件类
{
public:
Part();
Part(int i);
~Part();
void Print();
private:
int val;
};
class Whole
{
public:
Whole();
Whole(int i,int j,int k);
~Whole();
void Print();
private:
Part one;
Part two;
int date;
};
Whole::Whole()
{
date=0;
}
Whole::Whole(int i,int j,int k):
two(i),one(j),date(k)
{}
//...其它函数的实现略
前向引用声明:
如果在某个类声明之前,引用该类,应该进行前向引用声明。
class B; //前向引用声明
class A
{ public:
void f(B b);
};
class B
{ public:
void g(A a);
};
使用前向引用声明虽然可以解决一些问题,但它并不是万能的。需要注意的是,尽管使用了前向引用声明,但是在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象。请看下面的程序段:
class Fred; //前向引用声明
class Barney {
Fred x; //错误:类Fred的声明尚不完善
};
class Fred {
Barney y;
};
class Fred; //前向引用声明
class Barney {
public:
void method()
{
x->Do(); //错误:Fred类的对象在定义之前被使用
}
private:
Fred* x; //正确,经过前向引用声明,可以声明Fred类的对象指针
};
class Fred {
public:
void Do();
private:
Barney* y;
};
#include 包含指令
将一个源文件嵌入到当前源文件中该点处。
#include<文件名>
按标准方式搜索,文件位于C++系统目录的include子目录下
#include"文件名"
首先在当前目录中搜索,若没有,再按标准方式搜索。
#define 宏定义指令
定义符号常量,很多情况下已被const定义语句取代。
定义带参数宏,已被内联函数取代。
#undef
删除由#define定义的宏,使之不再起作用。
.h的开头和结尾
条件编译指令 #if 和 #endif
#if 常量表达式
//当“ 常量表达式”非零时编译
程序正文
#endif
......
条件编译指令——#else
#if 常量表达式
//当“ 常量表达式”非零时编译
程序正文1
#else
//当“ 常量表达式”为零时编译
程序正文2
#endif
条件编译指令 #elif
#if 常量表达式1
程序正文1 //当“ 常量表达式1”非零时编译
#elif 常量表达式2
程序正文2 //当“ 常量表达式2”非零时编译
#else
程序正文3 //其它情况下编译
#endif
#ifdef 标识符
程序段1
#else
程序段2
#endif
如果“标识符”经#defined定义过,且未经undef删除,则编译程序段1,否则编译程序段2。
多文件结构(例5-10)
一个源程序可以划分为多个源文件:
类声明文件(.h文件)
类实现文件(.cpp文件)
类的使用文件(main()所在的.cpp文件)
利用工程来组合各个文件。
//main.cpp
#include "file1.h"
#include "file2.h"
void main()
{
…
}
//file1.h 类使用文件
#include “point.h"
…
//file2.h 类使用文件
#include “point.h"
…
//point.h 类声明文件
…
class Point
{
…
};
…
//point.h
#ifndef POINT_H
#define POINT_H
…
class Point
{
…
};
…
#endif