C++:类与对象

类的定义

类是对一组具有共同属性特征和行为特征的对象的抽象,它可将相关数据和这些数据的操作(函数)组合在一起。

类定义格式

类定义格式:

class 类名
{
public:
	数据成员或成员函数
protected:
	数据成员或成员函数
private:
	数据成员或成员函数
};
  1. 类名

    class是声明类的关键字,类名是标识符,且在其作用域内必须是唯一的。

    注:C++规定,标识符以字母(大小写均可,但区分大小写)或下划线开头,后面跟0或多个有字母、数字字符或下划线组成的字符串。

  2. 成员说明

    类包括两类成员:一类是代表对象属性的数据成员;另一类是实现对象行为的成员函数。成员函数的定义不仅仅可以与声明同时在类内完成,也可以在类外完成。若在类外完成,则需用作用域‘::“符号。

  3. 访问权符

    访问权符也成为访问权限或访问控制符,它规定类中说明成员的访问属性。

    c++共提供三种不同的访问权符:public、protected、private。

    public(共有类型):声明该成员为公有成员。表示该成员哭可以被和该类对象处在同一作用域内的任何函数使用。一般将成员函数声明为共有的访问控制。

    protected(保护类型):声明该成员为保护成员。表示该成员只能被所在类中的成员函数及该类的友元函数使用。

    private(私有类型):声明该成员为私有成员。表示该成员只能被所在类中的成员函数和该类的友元函数使用。

声明一个图书类:

class Book{
public://访问权限,共有成员
    void inpub();//行为,成员函数的原型声明
    void print();//行为,成员函数的原型声明
private://访问权限,私有成员
    char title[12];//属性,数据成员
    char author[10];
    char publish[30];
    float price;
};

【注】:

  1. 类型命中的public、protected、private关键字可以按任意顺序出现,但为了让程序更加整洁,应将共有成员、保护成员和私有成员归类存放。默认的访问权限是私有类型。
  2. 对于一个具体的类,它们三个并不一定都要有,但至少需要有一个。
  3. 数据成员可以是任何数据类型,但不能用自动(auto)寄存器(register)外部(extern)类型进行说明。
  4. 类是一种广义的数据类型,系统并不会为其分配内存空间,所以不能在类声明中给数据成员赋初始值。
  5. 类的主体是包括在一对花括号中的,它的定义必须以”;“结束。

成员函数的定义

为了实现对象的行为,将一些相关的语句组织在一起,并给他们注明相应的名称,从而形成一些相对独立且方便管理和阅读的小块程序,每个小块程序都能描述一个完整的行为,这个小块程序就构成了成员函数。

成员函数的定义有两种形式:第一种是对于代码较少的成员函数,可以直接在类中定义;第二种是对于代码较多的成员函数,通常只在类中进行函数原型声明,在类外对函数进行定义,这个成员函数的定义格式如下:

返回类型  类名::函数名(参数表){//函数体}

例如上面的成员函数的定义:

void Book::inpub() {
    cin >> title >> author >> publish >> price;
}

void Book::print() {
    cout << title << " " << author << " " << publish << " " << price << endl;
}

对象的定义于使用

在C++中声明类表示定义了一种新的数据类型,只有定义了类的对象,才真正创建了这种数据类型的物理实体。对象是封装了数据结构及可以施加在这些数据结构上操作的封装体。对象是类的实际变量,一个具体的对象是类的一个实例。

对象的定义

有两种方法可以定义对象:

  1. 在声明类的同时,直接定义对象。

    class Book{
    public://访问权限,共有成员
        void input();//行为,成员函数的原型声明
        void print();//行为,成员函数的原型声明
    private://访问权限,私有成员
        char title[12];//属性,数据成员
        char author[10];
        char publish[30];
        float price;
    }b1;//定义b1是类Book的对象
    
  2. 先声明类,然后使用时在定义对象,定义格式于一般变量定义格式相同:类名 对象名列表;

对象的使用

使用对象就是像对象发送信息,请求执行其某个方法,从而向外界提供要求的服务,格式:

对象名.成员函数名(实参表);

Book b1;
b1.input();//通过b1执行输入操作
b1.print();//通过b1执行输出操作

对象的赋值

同类型的变量可以利用赋值运算符”=“进行赋值,对于同类型的对象也同样适用。这种赋值默认通过成员复制进行。当对象进行赋值时,对象的每个成员逐一复制给另一个对象的同一个成员。

例如平面上点的赋值。


#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;
}

运行结果:

p1:
x=1   y=2
p2:
x=-873984000   y=32759
p2=p1:
x=1   y=2

【注】:

  1. 在使用对象赋值语句进行赋值时,两个对象的类型必须相同,但赋值兼容规则除外。
  2. 两个对象间的赋值,仅使对象中的数据相同,二两个对象任然时彼此独立的,各自有自己的内存空间。
  3. 若类中存在指针,则不能简单地将一个对象的值赋值给另一个对象,否则会出错。

构造函数和析构函数

对对象进行初始化的一种方法就是编写初始化函数,然而很多用户在解决问题时,往往会忽略这些函数,以至于给朝鲜带来了隐患。为了方便对象的初始化工作,C++提供了两个特殊的成员函数,即构造函数和析构函数。构造函数的功能时在创建对象时,给数据成员赋初值,即对象的初始化。析构函数的功能时释放一个对象,在对象删除之前,用它来做一些内存释放等清理工作,它的功能于构造函数正好相反。

构造函数

在类的定义中不能直接对数据成员进行初始化,要想对对象中的数据成员进行初始化,一种方法是手动调用成员函数来完成初始化,但这会给程序员带来很大的负担。另一种就是使用构造函数。它是一种特殊的成员函数,是为对象分配空间、初始化,并且在对象创建时会被系统自动执行。

定义构造函数原型的格式:类名(形参列表);

在类外定义构造函数的格式:类名::类名(形参列表){//函数语句;}

构造函数特点如下:

  1. 构造函数的名字必须于类名相同,否则系统会将它当作一般的成员函数来处理。
  2. 构造函数可以用任意类型的参数,但是没有返回值类型,也不能指定void类型。
  3. 定义对象时,系统会自动地调用构造函数。
  4. 通常构造函数被定义在共有部分。
  5. 若没有定义构造函数,系统会自动生成一个默认的构造函数,它只负责对象的创建,不带任何参数,只能给对象开辟一个存储空间,数据成员的值也是随机的。
  6. 构造函数可以重载。

例如平面上点的赋值

#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;
}

【注】:

  1. 构造函数不能不带参数,例如:

    class Point{
    public:
    	Point(){x=0;y=0}
    	//...
    private:
    	int x, y;
    }
    
  2. 构造函数也可以采用构造初始化表对数据成员进行初始化,例如

    class Point{
    public:
    	Point(int a, int b):x(a),y(b){}
    	//构造函数初始化表对数据成员进行初始化
    	//...
    private:
    	int x, y;
    }
    
  3. 若数据成员时数组,则应该在构造函数种使用相关语句进行初始化,例如:

    class Point{
    public:
    	Point(char a[], int b):y(b){strcpy(x, strlen(a) + 1, a);}
    	//...
    private:
    	char x[10];
    	int  y;
    }
    

析构函数

析构函数时一种特殊的成员函数,当删除对象时就会被调用。也就是说,在对象的生命周期即将结束时,由系统自动调用,其目的是在系统回收对象内存之前执行结束清理工作,以便内存可被重新用于保存新对象。

定义析构函数的格式为:~类名();

析构函数的特点为:

  1. 析构函数名是由“~”和类名组成的。
  2. 析构函数没有参数,也没有返回值,而且不能重载。
  3. 通常析构函数被定义在共有部分,并有系统自动调用。
  4. 一个类中有且仅有一个析构函数,用为public

【注】:

  1. 析构函数的功能是释放对象所占用的内存空间,它在对象生命周期结束前由系统自动调动。
  2. 析构函数和构造函数两者的调用次序相反,即最先构造的对象最后被析构,最后构造的对象最先被析构
  3. 析构函数除了在显示撤销对象时,系统会自动调用外,函数结束时,函数内对象的析构函数也会被自动调用

内存的动态分配

用户存储区空间分为三部分:程序区(代码区)、静态存储区(数据区)和动态存储区(栈区和堆区)。代码区存放程序代码,程序有奴性前就可以分配存储空间。数据区存放常量、静态变量、全局变量等。栈区存放局部变量、函数参数、函数返回值和临时变量等。堆区是程序空间种存在的一些空闲存储单元。在堆中创建的数据对象称为堆对象。当创建对象时,堆中的一些存储单元从未分配状态变为已分配状态;当删除所创建的堆对象时,这些存储单元从已分配状态又变为未分配状态。当堆对象不再使用时,应给予删除,回收所占用的动态内存。C++中使用new和delete来实现在堆内存中进行数据的动态分配和释放。

运算符new

new功能时实现内存的动态分配。在程序运行过程中申请和释放的存储单元称为堆对象。申请和释放的过程称为建立和删除堆对象。

new的使用格式:

指针变量=new T;//T表示数据类型名
指针变量=new T(初值列表);
指针变量=new T[元素个数];

用new创建堆对象的格式:类名 *指针名 = new 类名([构造函数参数]);

【注】:

  1. new返回一个指定的合法数据类型内存空间的首地址(指针),若分配失败,则返回一个空指针。
  2. new不能对动态分配的数组存储去进行初始化。
  3. 用new分配的空间,使用结束后只能用delete来释放,否则这部分空间将不会被回收,从而造成内存泄漏。

运算符delete

delete用来释放动态变量或动态数组所占的内存空间,其格式为:

delete 指针变量名;
delete []指针变量名;

动态创建类Point的对象:

#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

#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;
}

运行结果:

1 个点的坐标为:(1,2)2 个点的坐标为:(3,4)3 个点的坐标为:(5,6)

对象指针

访问一个对象既可以通过对象名访问,也可以通过对象地址访问。对象指针就是用于存放对象地址的变量,它遵循一般变量指针的各种规则,对象指针的格式为:类名 *对象指针名;

与用对象名来访问对象成员一样,使用对象指针也可以访问对象的成员,格式:对象指针名 − > -> >成员名

例如,类Point

#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;
}

运行结果:

1 个点的坐标为:(1,2)2 个点的坐标为:(3,4)3 个点的坐标为:(5,6)

自引用指针this

this指针,即自引用指针,每当对象调用成员函数时,系统就将该对象的地址赋给this指针,这是编译器将根据this指针所指向的对象来确定应该引用哪个对象的数据成员。通常this指针在系统中时隐含地存在。

使用this指针应该注意:

  1. this指针是一个const指针,不能再程序中修改或给它赋值
  2. this指针是一个局部数据,其作用域仅在一个对象的内部
  3. 静态成员函数不属于任何一个对象,且没有this指针

你可能感兴趣的:(C++,c++)