第二章类和对象类 的概念,C++中的类就是一种用户自定义的数据类型,和其他数据类型不同的是,组成这种类型的不仅可以有数据,而且可以有对数据进行操作的函数,它们分别叫做类的数据成员和类的函数成员。
"定义性说明 "和 "引用性说明 ",定义性说明 就是 定义 类,说明了类的成员。而 引用性说明 就是只 说明 了类名,留待后面的代码对这个类加以定义。
一个 完整的类定义 包括关关键字 class 类名 { 类体 }在类体中为类定义了值域 (类中的数据成员的取值范围 )和操作 (类中的成员函数名 )。
class 类名
{
public:
若干成员;
protected:
若干成员;
private:
若干成员;
};
private public protected三种访问权限,类说明 (定义 )中任何成员 不能使用 extern,auto和 register关键字进行修饰。 不得 对类中数据成员进行 初始化 (赋初值 )等。
public的成员:一般是成员函数,用于定义类的外部接口,在程序中的任何部分都可访问。
private的成员:一般是数据成员,用于描述类的属性,它们只能被类自身的成员函数访问,类的成员默认情况下是私有的。
关键字 private public protected的出现顺序是任意的,并且可以不出现或出现多次。但类中的每个成员只能有一种特定的访问属性
Class Cylinder
{
public,
void setcylinder(double r,double h);
double getradius();
double getheight();
double volume();
double surface_area();
private:
double radius;
double height;
};
注:在类内不允许对声明的数据成员进行初始化此外,定义类之后,还要在程序中 定义类的成员函数的实现,它的一般形式为:
返回类型 类名::成员函数名 (参数说明 )
{函数体 }
类体中定义的是数据成员和成员函数,而这里的函数体定义的是成员函数的实现方法,但其形式是相同的,都是在 { }里的一段代码 。
注意 定义类 时,后要有分号 ; 表示类定义语句的结束 。
,::”是作用域运算符,它标明所要定义的函数属于哪个类注,1、如果函数体的定义放在类的声明内,这样定义的函数自动成为内联函数
2、如果在类外定义一个类的内联成员函数,应该在函数类型之前用关键字 inline进行说明
void Cylinder::setcylinder(double r,double h)
{
radius=r;
height=h;
}
double Cylinder::getradius()
{ return radius;}
double Cylinder::getheight()
{return height;}
double Cylinder::volume()
{double vol;
vol=3.1415926*radius*radius*height;
return vol;}
二,使用类和对象:
类是用户定义的一种类型,它并不是具体的对象,好比 "人 "是一个类,但 "人 "并不具体,我们通过 "人 "的特征可以找到具体的一个人比如 "张三 "这个人,他就是一个对象。类也一样,在程序中我们根据该类的类型说明一个变量,那么这个变量就是一个 对象,它是具体的,在内存中对象被分配相应的内存,它是以类作为样板生成的。
根据这个样板,可以 "做出 "许多的对象来,这些对象就称为 类的实例 。事实上建立对象时,只有每个对象的数据是分别占用内存的,
而操作这些数据的代码只有一份,由各对象共享,这是编译器实现对象的一种方法而已,我们仍应理解为一个对象是独立的由数据和代码组成。
1、对象的声明:
与一般变量相同,对象也必须经过声明后才能使用,声明类的对象如下:
类名 对象名;
Cylinder cylinder1,cylinder2;
Cylinder cylinder[10];
访问对象的成员可用
(1)对象名,数据成员名,对象名,成员函数名 (参数表 )
(2) 指向该类对象的指针 ->成员名
(3)对象的引用,成员名在 C++中,数据封装 是通过 类 来实现的。由于类中成员指定了访问权限,所以程序中其他函数就不能访问对象的私有成员,只能通过公有成员提供接口来访问。
class A
{ public:
int x;
setX(int a)
{
x=a;
}
getX()
{
return x;
};
A object;
object.x=5;
类的数据成员可以是类类型,也就是说 类的声明中数据成员可以是另一个类的对象,但必须注意以下两点:
1、这个对象不能是本类的对象。
2、作为数据成员的对象所属的类,应该在声明这个对象之前进行声明
class A
{
………….
}
class B
{
…………
A a;
……………….}
与建立函数原型相似,可以为类作 引用性声明 。这种声明只声明类名,即只为程序引入了一个类标识符,而标识符的实际意义在其他部分定义,这种引用性声明引入的标识符,在其实际意义未定义前只能被用于 声明函数原型 等场合
class B;
class A
{ public:
void fun(B b);
…………….};
class B
{
……………….
};
只经过引用性声明的类标识符,不能用于建立对象,因为引用性声明中未说明对象的数据结构,以及对数据的相关操作。
class B;
class A
{ public:
void fun(B b);
private:
B b; //错误,类 B尚未定义
…………….};
class B
{
……………….
};
构造函数与析构函数初始化 对象就是将对象的状态进行确定,即对其成员的取值进行确定。让一个对象一出现 (建立 )时就要有明确的状态。这就是对象的初始化。
怎么样才能让对象一建立就被初始化呢? 这并不同与对象建立以后再对其进行赋值的操作。
有一个方法就是使用 初始化列表,就是在定义对象的同时用一个 {
}中相应的值为对象进行初始化,使得这个对象一建立就有确定的取值。
构造函数定义 构造函数 的方法:
构造两字很专业,我们可以理解为是给对象自动进行初始化 (构建一个确定对象 )的函数。
构造函数是一个很特殊的函数:
a.构造函数的函数名与类名相同。 是成员函数,因此 构造函数 需在类中说明 (定义 )
b.构造函数没有返回类型。
c.构造函数的主要作用是完成对类对象的初始化工作。
d.构造函数不能由编程人员显示地直接调用。
e.在创建一个类的新对象的同时,系统会自动调用该类的构造函数为新对象初始化。
// Love.h 定 义类
class Love {
private,/ / 私有成员
int kiss;
public,//<font color=blue>构 造函数</font>
Love ( );
Love( int n);
};
// Love.cpp 成 员函数的实现
#include <iostream.h>
#incluce "Love.h"
Love,,Love( )
{
kiss=0;
cout << "默 认初始化kiss ="<< kiss << endl;
}
Love,,Love(int n)
{
kiss= n;
cout << "初 始化kiss=" << " " << kiss << endl;
}
/ / 以下是程序调用
void main( )
{
Love Me;
Love You( 10000);
}
构造函数可以不带行参
class Cylinder
{ public:
Cylinder();
void setcylinder(double r,double h)
double getradius();
double volume();
private:
double radius;
double height;}
Cylinder::Cylinder()
{ radius=5.0;height=10.0;}
构造函数可以带有形式参数:
class Cylinder
{ public:
Cylinder(double r,double h);
void setcylinder(double r,double h)
double getradius();
double volume();
private:
double radius;
double height;}
Cylinder::Cylinder(double r,double h)
{ radius=r;height=h;}
1如构造函数带有参数,在声明对象时,就必须利用实参对对象进行初始化。例 Cylinder cylinder1(2.0,3.0)
若对象是数组,Cylinder cylinder1[3]={Cylinder(1.0,2.0),
Cylinder(3.0,4.0),Cylinder(5.0,6.0))
2如构造函数不带参数,括号应省略。
例,Cylinder cylinder1;
3与普通函数一样,构造函数也可以定义为内联函数,可以带默认形参值,也可以重载。
Cylinder();
Cylinder(double r,double h);
在主函数中有下述语句:
Cylinder cylinder1(5.0,10.0),cylinder2;
注:当类中没有声明构造函数时,系统会为之生成一个不带参数的构造函数,该函数体内没有任何语句,不执行任何操作:
类名,:类名()
{ }
2、析构函数;
(1)对象终止时由系统调用,以释放分配给对象的内存。
(2)函数名与类名相同,在函数名前加,~”,没有形参。
(3)一个类内只能声明一个析构函数。是公有的成员函数,如类中没有声明,编译器也会自动生成一个带空函数体的析构函数。
对象成员:
在一个类中说明具有类的类型的数据成员,这些成员就称为 对象成员 。定义的方法是:
class 类名 {
类名 1 成员名 1 ;//事实上就是一个对象,这个对象的类型和名字类名 2 成员名 2 ;
...
类名 n 成员名 n;
其他成员的类型 和 成员名 ;
}
对于有对象成员的类的初始化,也要有 构造函数 。定义的方法是:
X,,X(参数表 0):成员 1(参数表 1),成员 2(参数表 2),成员 n(参数表 n)
{
//...
}
初始化 有对象成员的类 的时候,先调用对象成员 的 构造函数,对成员进行初始化,再执行 类 的 构造函数,对类中 其他成员 进行初始化。
析构函数 的调用顺序正好相反。
说明:在创建组合类的对象时,将按内嵌对象在类中声明的先后顺序,而不是按成员初始化列表中的先后顺序,调用其相应的构造函数,最后调用该组合类的构造函数。而析构函数的调用顺序相反。
内部数据类型构造函数:成员初始化列表也可以初始化类中的普通数据成员,有时还是必须的
class A
{ public:
A(int x):a(x)
{cout<<“class A constructing\t\t”<<a<<endl;}
~A()
{ cout<<“class A destructing”<<endl;}
private:
int a;};
class integer{
int I;
public:
integer(int I=0);
};
integer::integer(int I):I(I) {}
如果建立一个 integer数组,它们都被自动化为零:
integer I[100];
与 for循环相比,这种初始化不必付出更多的开销。很多编译器可以很容易把它优化成一个很快的过程。