类和对象
OOP第二课
- 1 类的构成
- 1.1 从结构到类
- 1.2 类的构成
- 2 成员函数的声明
- 2.1 普通成员函数形式
- 2.2 将成员函数以内联函数的形式进行说明
- 3 对象的定义和使用
- 3.1 对象的定义
- 3.2 对象中成员的访问
- 3.3 类成员的访问属性
- 3.4 类中成员的访问方式
- 3.5 对象赋值语句
- 3.6 类的作用域
类(class)是面向对象程序设计的最基本的概念,是C++最强有力的特征,是进行封装和数据隐藏的工具,它将一个数据结构与一个操作集紧密地结合起来。类对象是类的实例,用类对象模拟现实世界中的事物比用数据对象更确切。
1. 类的构成
1.1 从结构到类
结构是C的一种自定义的数据类型,它把相关联的数据元素组成一个单独的统一体。
例2.1有关日期结构的例子
#include <iostream.h>
struct Date {
int year;
int month;
int day;
};
int main()
{
Date date1;
date1.year=2003;
date1.month=8;
date1.day=25;
cout<<date1.year<<"."<<date1.month<<"."
<<date1.day<<endl;
return 0;
}
⭐class与struct的比较
- 类是C++对C中结构的扩展。
- C语言中的struct是数据成员集合,而C++中的类,则是数据成员和成员函数的集合。
- struct是用户定义的数据类型,是一种构造数据类型。类和struct一样,也是一种用户定义的数据类型,是一种构造数据类型。
- C结构无法对数据进行保护和权限控制,所以结构中的数据是不安全的。C++中的类将数据和与之相关联的数据封装在一起,形成一个整体,具有良好的外部接口可以防止数据未经授权的访问,提供了模块间的独立性。
1.2 类的构成
类的成员分两部分:一部分对应数据的状态,称为数据成员,另一部分为作用于该数据状态的函数,称为成员函数。
类声明的一般格式如下:
class 类名 {
public:
公有数据成员;
公有成员函数;
protected:
保护数据成员;
保护成员函数;
private:
私有数据成员;
私有成员函数;
};
3个关键字: private
、protected
和public
,称为访问权限关键字。每个关键字下面又都可以有数据成员和成员函数。
private
部分称为类的私有部分,这一部分的数据成员和成员函数称为类的私有成员。私有成员只能由本类的成员函数访问,而类外部的任何访问都是非法的。public
部分称为类的公有部分,这部分的数据成员和成员函数称为类的公有成员。公有成员可以由程序中的函数访问,它对外是完全开放的。protected
部分称为类的保护部分,这部分的数据成员和成员函数称为类的保护成员。保护成员可以由本类的成员函数访问,也可以由本类的派生类的成员函数访问,而类外的任何访问都是非法的。
注意:
- (1) 类声明格式中的3个部分并非一定要全有,但至少要有其中的一个部分。
- 一般一个类的数据成员应该声明为私有成员,成员函数声明为公有成员。
- (2) 类声明中的
private
、protected
和public
三个关键字可以按任意顺序出现任意次。但是,如果把所有的私有成员、保护成员和公有成员归类放在一起,程序将更加清晰。 - (3)
private
处于类体中第一部分时, 关键字private
可以省略。 - (4) 数据成员可以是任何数据类型,但不能用自动(auto)、寄存器(register)或外部(extern)进行声明。
- (5) 不能在类声明中给数据成员赋值。C++规定,只有在类对象定义之后才能给数据成员赋初值
用一个类来描述日期,其形式如下:
class Date {
public:
void setDate(int y,int m,int d);
void showDate();
private:
int m_year;
int m_month;
int m_day;
};
2. 成员函数的声明
成员函数的声明通常采用以下两种方式:
2.1 普通成员函数形式
即在类的声明中只给出成员函数的原型,而成员函数体写在类的外部。
返回类型 成员函数 (参数列表);
类外定义的一般形式是:
返回类型 类名∷成员函数名(参数表)
{
// 函数体
}
例如, 以下是表示坐标点的类Coord的声明
class Coord {
public:
void setCoord (int,int); // 设置坐标点
int getx(); // 取x坐标点
int gety(); // 取y坐标点
private:
int x,y;
};
void Coord∷setCoord(int a,int b){ x=a; y=b;}
int Coord::getx(){ return x;}
int Coord::gety(){ return y;}
内联函数和外联函数
类的成员函数可以分为内联函数和外联函数。
- 内联函数是指那些定义在类体内的成员函数,即该函数的函数体放在类体内。而说明在类体内,定义在类体外的成员函数叫外联函数。外联函数的函数体在类的实现部分。
- 内联函数在调用时不是像一般函数那样要转去执行被调用函数的函数体,执行完成后再转回调用函数中,执行其后语句,而是在调用函数处用内联函数体的代码来替换,这样将会节省调用开销,提高运行速度。
- 内联函数与带参数的宏定义进行一下比较,它们的代码效率是一样的,但是内联函数要优于宏定义,因为内联函数遵循函数的类型和作用域规则,它与一般函数更相近,在一些编译器中,一旦关上内联扩展,将与一般函数一样进行调用,调试比较方便。
- 外联函数变成内联函数的方法很简单,只要在函
数头前面加上关键字inline就可以了。
2.2 将成员函数以内联函数的形式进行说明
有两种格式将成员函数声明为类的内联函数:
- (1)隐式声明 直接将函数声明在类内部。
class Coord{
public:
void setCoord(int a,int b)
{ x=a; y=b;}
int getx()
{ return x;}
int gety()
{ retrun y;}
private:
int x,y;
};
- (2)显式声明
在类声明中只给出成员函数的原型,而成员函数体写在类的外部。为了使它起内联函数的作用,在成员函数返回类型前冠以关键字“inline”。
这种成员函数在类外定义的一般形式是:
inline 返回类型 类名::成员函数名(参数表)
{
// 函数体
}
class Coord{
public:
void setCoord(int,int);
int getx();
int gety();
private:
int x,y;
};
inline void Coord::setCoord(int a,int b)
{ x=a; y=b;}
inline int Coord::getx(){ return x;}
inline int Coord::gety(){ return y; }
说明
- (1)使用inline说明内联函数时,必须使函数体和inline说明结合在一起,否则编译器将它作为普通函数处理。
inline void Coord:: setCoord(int ,int);
// 不能说明这是一个内联函数
- (2) 通常只有较短的成员函数才定义为内联函数,对较长的成员函数最好作为一般函数处理。
class Date{
public:
void showDate();
private:
int year;
int month;
int day;
};
inline void Date::showDate()
{cout<<year<<'.';<<month<<'.'<<day<<endl;}
3. 对象的定义和使用
类与对象的关系
通常我们把具有共同属性和行为的事物所构成的集合叫做类。
在C++中,可以把相同数据结构和相同操作集的对象看成属于同一类。
在C++中,类也是一种用户自定义数据类型,类的对象可以看成是该类类型的一个实例,定义一个对象和定义一个变量相似。类与对象间的关系,可以用数据类型int和整型变量i之间的关系类比。
C++把类的变量叫做类的对象,对象也称为类的实例。
3.1 对象的定义
对象的定义, 也称对象的创建
在C++中可以用以下两种方法定义对象:
(1) 在声明类的同时,直接定义对象。
class Coord {
public:
void setCoord(int,int);
int getx();
int gety();
private:
int x,y;
} op1,op2;
(2) 声明了类之后,在使用时再定义对象
class Coord {
//…
};
// …
main()
{
Coord op1,op2;
// …
}
说明:
- 在声明类的同时定义的对象是一种全局对象,在它的生存期内任何函数都可以使用它。
- 声明了一个类便声明了一种类型,它并不接收和存储具体的值,只作为生成具体对象的一种“样板”,只有定义了对象后,系统才为对象分配存储空间。
3.2 对象中成员的访问
当定义了一个类的对象后,就可以访问对象的成员了。在类的外部可以通过类的对象对公有成员进行访问,访问对象成员要使用操作符“.”。
访问的一般形式是:
- 对象名.数据成员名
- 对象名.成员函数名(参数表)
其中“.”叫做对象选择符,简称点运算符。
例2.2 使用类Coord的完整程序
#include<iostream.h>
class Coord {
public:
void setCoord(int a,int b)
{ x=a; y=b; }
int getx()
{ return x; }
int gety()
{ return y; }
private:
int x,y;
};
void main()
{
Coord op1,op2;
int i,j;
op1.setCoord(5,6); // 调用op1的setCoord(),初始化对象op1
op2.setCoord(7,8); // 调用op2的setCoord(),初始化对象op2
i=op1.getx(); // 调用op1的getx(),取op1的x值
j=op1.gety(); // 调用op1的gety(),取op1的y值
cout<<"op1 i= "<<i<<" op1 j= "<<j<<endl;
i=op2.getx(); // 调用op2的getx(),取op2的x值
j=op2.gety(); // 调用op2的gety(),取op2的y值
cout<<"op2 i= "<<i<<" op2 j= "<<j<<endl;
}
说明:
- 对象名.成员名实际上是一种缩写形式,它表达的意义是:对象名.类名::成员名
void main( )
{
Date date1;
date1.setDate(2006,9,21);
//……
}
- 在类的内部所有成员之间都可以通过成员函数直接访问,但是类的外部不能访问对象的私有成员。
例2.3 一个存在错误的程序
#include <iostream.h>
class Date {
public:
void setDate(int y,int m,int d);
void showDate();
private:
int year;
int month;
int day;
};
void Date∷setDate(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
inline void Date∷showDate()
{
cout<<year<<"."<<month<<"."<<day<<endl;
}
void main()
{
Date date1,date2;
cout<<"Date1 set and output:"<<endl;
date1.setDate(1998,4,28);
cout<<date1.year<<"."<<date1.month<<"."<<date1.day<<endl; //错误
date1.showDate();
cout<<"Date2 set and output: "<<endl;
date2.setDate(2002,11,14);
cout<<date2.year<<"."<<date2.month<<"."<<date2.day<<endl; //错误
date2.showDate();•
}
- 在定义对象时,若定义的是指向对象的指针,则访问此对象的成员时,要用“->”操作符。
void main( )
{
Date *date3;
date3->setDate(2001,8,15);
//……
}
3.3 类成员的访问属性
类成员有三种访问属性:公有(public)、 私有(private) 和保护(protected) 。
- 说明为公有的成员不但可以被类中成员函数访问;还可在类的外部,通过类的对象进行访问。
- 说明为私有的成员只能被类中成员函数访问,不能在类的外部,通过类的对象进行访问。
- 说明为保护的成员除了类本身的成员函数可以访问外,该类的派生类的成员也可以访问,但不能在类的外部,通过类的对象进行访问。类成员的访问属性
类的成员对类对象的可见性和对类的成员函数的可见性是不同的。
类的成员函数可以访问类的所有成员,而类的对象对类的成员的访问是受类成员的访问属性的制约的。
3.4 类中成员的访问方式
- 类中成员互访:直接使用成员名
- 类外访问:使用“对象名.成员名”方式访问 public 属性的成员
class Sample{
public:
int k;
int geti(){return i;}
int getj(){return j;}int getk(){return k;}
private:
int i;
protected:
int j;
};
//……
Sample a; //定义类Sample的对象a
a.i; //非法,类Sample的对象a不能访问类的私有成员i
a.j; //非法,类Sample的对象a不能访问类的保护成员j
a.k; //合法,类Sample的对象a能访问类的公有成员k
//……
一般来说,公有成员是类的对外接口,而私有成员和保护成员是类的内部数据和内部实现,不希望外界访问。
将类的成员划分为不同的访问级别有两个好处:一是信息隐蔽,即实现封装;二是数据保护,即将类的重要信息保护起来,以免其它程序不恰当地修改。
3.5 对象赋值语句
两个同类型的变量之间可以相互赋值。同类型的对象间也可以进行赋值,当一个对象赋值给另一个对象时,所有的数据成员都会逐位拷贝。
例2.5
#include<iostream.h>
class abc{
public:
void init(int i,int j) { a=i; b=j; }
void show(){ cout<<a<<" "<<b<<endl; }
private:
int a,b;
};
main()
{
abc o1,o2;
o1.init(12,34);
o2=o1; // 将对象o1数据成员的值赋给对象o2
o1.show();
o2.show();
return 0;
}
说明:
- 在使用对象赋值语句进行对象赋值时,两个对象的类型必须相同,如果对象的类型不同,编译时将出错。
- 两个对象之间的赋值,仅仅使这些对象中数据成员相同,而两个对象仍是分离的。
- 例2.5的对象赋值是通过缺省的赋值运算符函数实现的。
- 当类中存在指针时,使用缺省的赋值运算符进行对象赋值,可能会产生错误。
3.6 类的作用域
所谓类的作用域就是指在类声明中的一对花括号所形成的作用域。
- 一个类的所有成员都在该类的作用域内,一个类的任何成员可以访问该类的其他成员。
- 一个类的成员函数可以不受限制地访问类的成员,而在类的外部,对该类的数据成员和成员函数的访问则要受到一定的限制,有时甚至是不允许的,这体现了类的封装功能。
例2.6 理解类的作用域
# include<iostream.h>
class myclass{
public:
int i;
void init(int);
void show(){ cout<<“i=” <<i<<endl;} // 可以访问类中的数据成员i
};
void myclass::init(int si){ i=si;} // 可以访问类中的数据成员i
int fun(){ return i; } // 非法,不能直接访问类中的i
void main()
{
myclass ob;
ob.init(5); // 给数据成员i赋初值5
ob.show();
i=8; // 非法,不能直接访问类中的i,可改写成ob.i=8
ob.show();
}