类的定义
类是对一组具有共同属性特征和行为特征的对象的抽象,它可将相关数据和这些数据的操作(函数)组合在一起。
类定义格式
类定义格式:
cpp
class 类名
{
public:
数据成员或成员函数
protected:
数据成员或成员函数
private:
数据成员或成员函数
};
-
类名:
class是声明类的关键字,类名是标识符,且在其作用域内必须是唯一的。
注:C++规定,标识符以字母(大小写均可,但区分大小写)或下划线开头,后面跟0或多个有字母、数字字符或下划线组成的字符串。
-
成员说明
类包括两类成员:一类是代表对象属性的数据成员;另一类是实现对象行为的成员函数。成员函数的定义不仅仅可以与声明同时在类内完成,也可以在类外完成。若在类外完成,则需用作用域'::"符号。
-
访问权符
访问权符也成为访问权限或访问控制符,它规定类中说明成员的访问属性。
c++共提供三种不同的访问权符:public、protected、private。
public(共有类型):声明该成员为公有成员。表示该成员哭可以被和该类对象处在同一作用域内的任何函数使用。一般将成员函数声明为共有的访问控制。
protected(保护类型):声明该成员为保护成员。表示该成员只能被所在类中的成员函数及该类的友元函数使用。
private(私有类型):声明该成员为私有成员。表示该成员只能被所在类中的成员函数和该类的友元函数使用。
声明一个图书类:
cpp
class Book{
public://访问权限,共有成员
void inpub();//行为,成员函数的原型声明
void print();//行为,成员函数的原型声明
private://访问权限,私有成员
char title[12];//属性,数据成员
char author[10];
char publish[30];
float price;
};
【注】:
- 类型命中的public、protected、private关键字可以按任意顺序出现,但为了让程序更加整洁,应将共有成员、保护成员和私有成员归类存放。默认的访问权限是私有类型。
- 对于一个具体的类,它们三个并不一定都要有,但至少需要有一个。
- 数据成员可以是任何数据类型,但不能用自动(auto) 、寄存器(register)或外部(extern)类型进行说明。
- 类是一种广义的数据类型,系统并不会为其分配内存空间,所以不能在类声明中给数据成员赋初始值。
- 类的主体是包括在一对花括号中的,它的定义必须以";"结束。
成员函数的定义
为了实现对象的行为,将一些相关的语句组织在一起,并给他们注明相应的名称,从而形成一些相对独立且方便管理和阅读的小块程序,每个小块程序都能描述一个完整的行为,这个小块程序就构成了成员函数。
成员函数的定义有两种形式:第一种是对于代码较少的成员函数,可以直接在类中定义;第二种是对于代码较多的成员函数,通常只在类中进行函数原型声明,在类外对函数进行定义,这个成员函数的定义格式如下:
cpp
返回类型 类名::函数名(参数表){//函数体}
例如上面的成员函数的定义:
cpp
void Book::inpub() {
cin >> title >> author >> publish >> price;
}
void Book::print() {
cout << title << " " << author << " " << publish << " " << price << endl;
}
对象的定义于使用
在C++中声明类表示定义了一种新的数据类型,只有定义了类的对象,才真正创建了这种数据类型的物理实体。对象是封装了数据结构及可以施加在这些数据结构上操作的封装体。对象是类的实际变量,一个具体的对象是类的一个实例。
对象的定义
有两种方法可以定义对象:
-
在声明类的同时,直接定义对象。
cppclass Book{ public://访问权限,共有成员 void input();//行为,成员函数的原型声明 void print();//行为,成员函数的原型声明 private://访问权限,私有成员 char title[12];//属性,数据成员 char author[10]; char publish[30]; float price; }b1;//定义b1是类Book的对象
-
先声明类,然后使用时在定义对象,定义格式于一般变量定义格式相同:类名 对象名列表;
对象的使用
使用对象就是像对象发送信息,请求执行其某个方法,从而向外界提供要求的服务,格式:
对象名.成员函数名(实参表);
cpp
Book b1;
b1.input();//通过b1执行输入操作
b1.print();//通过b1执行输出操作
对象的赋值
同类型的变量可以利用赋值运算符"="进行赋值,对于同类型的对象也同样适用。这种赋值默认通过成员复制进行。当对象进行赋值时,对象的每个成员逐一复制给另一个对象的同一个成员。
例如平面上点的赋值。
cpp
#include "iostream"
#include "iomanip"
using namespace std;
class Point{
public:
void SetPoint(int a, int b);
void Print(){
cout << "x=" << x << setw(5) << "y=" << y << endl;
}
private:
int x, y;
};
void Point::SetPoint(int a, int b) {
x = a;
y = b;
}
int main(){
Point p1, p2;
p1.SetPoint(1, 2);
cout << "p1:" << endl;
p1.Print();
cout << "p2:" << endl;
p2.Print();
p2 = p1;
cout << "p2=p1:" << endl;
p2.Print();
return 0;
}
运行结果:
cpp
p1:
x=1 y=2
p2:
x=-873984000 y=32759
p2=p1:
x=1 y=2
【注】:
- 在使用对象赋值语句进行赋值时,两个对象的类型必须相同,但赋值兼容规则除外。
- 两个对象间的赋值,仅使对象中的数据相同,二两个对象任然时彼此独立的,各自有自己的内存空间。
- 若类中存在指针,则不能简单地将一个对象的值赋值给另一个对象,否则会出错。
构造函数和析构函数
对对象进行初始化的一种方法就是编写初始化函数,然而很多用户在解决问题时,往往会忽略这些函数,以至于给朝鲜带来了隐患。为了方便对象的初始化工作,C++提供了两个特殊的成员函数,即构造函数和析构函数。构造函数的功能时在创建对象时,给数据成员赋初值,即对象的初始化。析构函数的功能时释放一个对象,在对象删除之前,用它来做一些内存释放等清理工作,它的功能于构造函数正好相反。
构造函数
在类的定义中不能直接对数据成员进行初始化,要想对对象中的数据成员进行初始化,一种方法是手动调用成员函数来完成初始化,但这会给程序员带来很大的负担。另一种就是使用构造函数。它是一种特殊的成员函数,是为对象分配空间、初始化,并且在对象创建时会被系统自动执行。
定义构造函数原型的格式:类名(形参列表);
在类外定义构造函数的格式:类名::类名(形参列表){//函数语句;}
构造函数特点如下:
- 构造函数的名字必须于类名相同,否则系统会将它当作一般的成员函数来处理。
- 构造函数可以用任意类型的参数,但是没有返回值类型,也不能指定void类型。
- 定义对象时,系统会自动地调用构造函数。
- 通常构造函数被定义在共有部分。
- 若没有定义构造函数,系统会自动生成一个默认的构造函数,它只负责对象的创建,不带任何参数,只能给对象开辟一个存储空间,数据成员的值也是随机的。
- 构造函数可以重载。
例如平面上点的赋值
cpp
#include "iostream"
#include "iomanip"
using namespace std;
class Point{
public:
Point(int a, int b){
x = a;
y = b;
}
void Print(){
cout << "x=" << x << setw(5) << "y=" << y << endl;
}
private:
int x, y;
};
int main(){
Point p1(1,2);
cout << "p1:" << endl;
p1.Print();
return 0;
}
【注】:
-
构造函数不能不带参数,例如:
cppclass Point{ public: Point(){x=0;y=0} //... private: int x, y; }
-
构造函数也可以采用构造初始化表对数据成员进行初始化,例如
cppclass Point{ public: Point(int a, int b):x(a),y(b){} //构造函数初始化表对数据成员进行初始化 //... private: int x, y; }
-
若数据成员时数组,则应该在构造函数种使用相关语句进行初始化,例如:
cppclass Point{ public: Point(char a[], int b):y(b){strcpy(x, strlen(a) + 1, a);} //... private: char x[10]; int y; }
析构函数
析构函数时一种特殊的成员函数,当删除对象时就会被调用。也就是说,在对象的生命周期即将结束时,由系统自动调用,其目的是在系统回收对象内存之前执行结束清理工作,以便内存可被重新用于保存新对象。
定义析构函数的格式为:~类名();
析构函数的特点为:
- 析构函数名是由"~"和类名组成的。
- 析构函数没有参数,也没有返回值,而且不能重载。
- 通常析构函数被定义在共有部分,并有系统自动调用。
- 一个类中有且仅有一个析构函数,用为public
【注】:
- 析构函数的功能是释放对象所占用的内存空间,它在对象生命周期结束前由系统自动调动。
- 析构函数和构造函数两者的调用次序相反,即最先构造的对象最后被析构,最后构造的对象最先被析构
- 析构函数除了在显示撤销对象时,系统会自动调用外,函数结束时,函数内对象的析构函数也会被自动调用
内存的动态分配
用户存储区空间分为三部分:程序区(代码区)、静态存储区(数据区)和动态存储区(栈区和堆区)。代码区存放程序代码,程序有奴性前就可以分配存储空间。数据区存放常量、静态变量、全局变量等。栈区存放局部变量、函数参数、函数返回值和临时变量等。堆区是程序空间种存在的一些空闲存储单元。在堆中创建的数据对象称为堆对象。当创建对象时,堆中的一些存储单元从未分配状态变为已分配状态;当删除所创建的堆对象时,这些存储单元从已分配状态又变为未分配状态。当堆对象不再使用时,应给予删除,回收所占用的动态内存。C++中使用new和delete来实现在堆内存中进行数据的动态分配和释放。
运算符new
new功能时实现内存的动态分配。在程序运行过程中申请和释放的存储单元称为堆对象。申请和释放的过程称为建立和删除堆对象。
new的使用格式:
cpp
指针变量=new T;//T表示数据类型名
指针变量=new T(初值列表);
指针变量=new T[元素个数];
用new创建堆对象的格式:类名 *指针名 = new 类名([构造函数参数]);
【注】:
- new返回一个指定的合法数据类型内存空间的首地址(指针),若分配失败,则返回一个空指针。
- new不能对动态分配的数组存储去进行初始化。
- 用new分配的空间,使用结束后只能用delete来释放,否则这部分空间将不会被回收,从而造成内存泄漏。
运算符delete
delete用来释放动态变量或动态数组所占的内存空间,其格式为:
cpp
delete 指针变量名;
delete []指针变量名;
动态创建类Point的对象:
cpp
#include "iostream"
using namespace std;
class Point{
public:
Point(int a, int b){
x = a;
y = b;
cout << "inside constructor" << endl;
}
~Point(){
cout << "inside destructor" << endl;
}
private:
int x, y;
};
int main(){
Point *p = new Point(1, 2);
delete p;
return 0;
}
对象数组和数组指针
对象数组
数组的元素既可以时基本数据类型的数据,也可以是用户自定义数据类型的数据。对象数组是指每个数组元素都是对象的数组。对象数组的元素是对象,它不仅具有数据成员,而且还有成员函数。
声明对象数组的格式为:类名 数组名[下标表达式];
与基本类型的数组一样,在使用对象数组时也只能引用单个数组元素,通过对象可以方位其公有成员。对象数组的引用格式为:数组名[下标].成员函数
例如类Point
cpp
#include "iostream"
using namespace std;
class Point{
public:
Point(int a, int b){
x = a;
y = b;
}
void Print(){
cout << "(" << x << "," << y << ")" << endl;
}
private:
int x, y;
};
int main(){
Point ob[3] = {Point(1,2),Point(3,4), Point(5, 6)};
for(int i = 0; i < 3; i++){
cout << "第 " << i + 1 << " 个点的坐标为:";
ob[i].Print();
}
return 0;
}
运行结果:
cpp
第 1 个点的坐标为:(1,2)
第 2 个点的坐标为:(3,4)
第 3 个点的坐标为:(5,6)
对象指针
访问一个对象既可以通过对象名访问,也可以通过对象地址访问。对象指针就是用于存放对象地址的变量,它遵循一般变量指针的各种规则,对象指针的格式为:类名 *对象指针名;
与用对象名来访问对象成员一样,使用对象指针也可以访问对象的成员,格式:对象指针名 − > -> −>成员名
例如,类Point
cpp
#include "iostream"
using namespace std;
class Point{
public:
Point(int a, int b){
x = a;
y = b;
}
void Print(){
cout << "(" << x << "," << y << ")" << endl;
}
private:
int x, y;
};
int main(){
Point ob[3] = {Point(1,2),Point(3,4), Point(5, 6)};
Point *p =ob;
for(int i = 0; p < ob + 3; p++, i++){
cout << "第 " << i + 1 << " 个点的坐标为:";
p->Print();
}
return 0;
}
运行结果:
cpp
第 1 个点的坐标为:(1,2)
第 2 个点的坐标为:(3,4)
第 3 个点的坐标为:(5,6)
自引用指针this
this指针,即自引用指针,每当对象调用成员函数时,系统就将该对象的地址赋给this指针,这是编译器将根据this指针所指向的对象来确定应该引用哪个对象的数据成员。通常this指针在系统中时隐含地存在。
使用this指针应该注意:
- this指针是一个const指针,不能再程序中修改或给它赋值
- this指针是一个局部数据,其作用域仅在一个对象的内部
- 静态成员函数不属于任何一个对象,且没有this指针