类与对象(一)

  1. 对象
  2. 构造函数
  3. 析构函数
  4. 对象生存期

类的声明

类作为另一种结构化的数据类型,与结构体不同的是,类专门设计用来组织数据和函数的

一般格式:

class 类名

{

private:

​ 私有数据成员和成员函数;

protected:

​ 保护数据成员和成员函数;

public:

​ 公有数据成员和成员函数;

};

各个成员函数的实现;

类定义是以关键字 class 开头,后跟类的名称。类的主体是包含在一对花括号中。类定义后必须跟着一个分号或一个声明列表

class myclass
{
private:                     //声明成员
   int x,y;
public:
   void setvalue(int x1,int y1);
   void display();
};
void myclass::setvalue(int x1,int y1)  //实现成员函数
{
x=x1;y=y1;
}
void myclass::display()    //实现成员函数
{
cout<<"x="<<x<<"y="<<y<<endl;
}

该类包含两个私有成员x,y和两个公有成员函数。公有函数中包含x和y。

需要从类的外部访问成员应该被定义为公有的,不能从外部访问的定义为私有的,x和y称为这个类的数据成员不能通过对象修改他们的值。

类是一种数据类型,在声明一个类时系统并不会为其分配空间,所以定义类中的数据成员时,不能对其进行初始化。

类的组织形式

通常将类界面与类实现分离,类界面部分放在头文件中<.h>。将类的实现放在(.cpp)中,而使用类的程序放在另一个程序文件中,这样使整个程序更加清晰。

类的成员函数可以访问到类的所有成员,没有任何限制,而类的对象对类的成员的访问受成员访问控制符制约

一般来说,公有成员是类的对外接口,而私有成员和保护成员是类的内部实现的,不希望外界了解,C++没有私有成员和公有成员的顺序限制。

类与结构体

c++中类是由结构体烟花而来的,但对结构体进行了扩展,结构体的成员也可以是数据成员和函数成员而且在结构体中也可以使用public、private、protected限制成员访问权限。唯一的区别在于,在类中,其成员函数访问权限是默认为私有的,在结构体中,成员的默认访问权限是共有的。

当只要描述数据结构时,使用结构体较好,当需要描述数据又需描述对数据的处理方法时使用类比较好。

对象

类提供了对象的蓝图,所以基本上,对象是根据类来创建的。声明类的对象,就像声明基本类型的变量一样。下面的语句声明了类 Box 的两个对象:

Box Box1;          // 声明 Box1,类型为 Box
Box Box2;          // 声明 Box2,类型为 Box

成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义。在类定义中定义的成员函数把函数声明为内联的,即便没有使用 inline 标识符。所以您可以按照如下方式定义 Volume() 函数:

class Box
{
   public:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度
   
      double getVolume(void)
      {
         return length * breadth * height;
      }
};

您也可以在类的外部使用范围解析运算符 :: 定义该函数,如下所示:

double Box::getVolume(void)
{
    return length * breadth * height;
}

在这里,需要强调一点,在 :: 运算符之前必须使用类名。调用成员函数是在对象上使用点运算符(.),这样它就能操作与该对象相关的数据,如下所示:

Box myBox;          // 创建一个对象
 
myBox.getVolume();  // 调用该对象的成员函数

那么内联函数又是什么呢?

内联(inline)是内联扩展的简称,c++编辑器在遇到内联函数的地方会用函数体中的代码替换函数的调用。内联函数定义是在一般函数定义前加inline关键字

inline int abs(int x)
{if (x<0) return -x;
  else return x;
}

void main()
{int m,m1=2,n,n1=-10;
 m=abs(m1);
 n=abs(n1);
 cout<<"m="<<m<<"n="<<n<<endl;
}

对象数据成员的访问方式为对象名.数据成员名,通过对象指针访问对象数据成员的方式为对象指针名->数据成员名。或者(*对象指针名).数据成员名。

#include<iostream.h>
class TPoint
{
    int x,y;
 public:
    void set(int x1,int y1)
    {
        x=x1;y=y1;
    }
    void dispoint()
    {
        cout<<x<<y<<endl;
    }
};
void main()
{
    TPoint a;
    TPoint *p=new TPoint;
    a.set(12,6);
    cout<<"First point=";
    a.dispoint();
    p->set(5,12);
    cout<<"Second point=";
    p->dispont();
}

访问成员

类的对象的公共数据成员可以使用直接成员访问运算符 (.) 来访问。为了更好地理解这些概念,让我们尝试一下下面的实例:

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
};
 
int main( )
{
   Box Box1;        // 声明 Box1,类型为 Box
   Box Box2;        // 声明 Box2,类型为 Box
   double volume = 0.0;     // 用于存储体积
 
   // box 1 详述
   Box1.height = 5.0; 
   Box1.length = 6.0; 
   Box1.breadth = 7.0;
 
   // box 2 详述
   Box2.height = 10.0;
   Box2.length = 12.0;
   Box2.breadth = 13.0;
 
   // box 1 的体积
   volume = Box1.height * Box1.length * Box1.breadth;
   cout << "Box1 的体积:" << volume <<endl;
 
   // box 2 的体积
   volume = Box2.height * Box2.length * Box2.breadth;
   cout << "Box2 的体积:" << volume <<endl;
   return 0;
}

构造函数

  1. 构造函数主要用于对象成员的初始化工作
class Myclass2
{
    private:
       int x,y;
       public:
       Myclass2();     //声明构造函数1
       Myclass2(int a,int b);       //声明构造函数2
       void setvalue(int x1,int y1);  //声明成员函数
       void display();
};

Myclass::Myclass2()    //构造函数1的实现
{
    x=0;y=0;
}
Myclass::Myclass2(int a,int b)   //构造函数2的实现
{
    x=a;y=b;
}

其中构造函数1是默认的构造函数,它将Myclass2类对象的两个数据成员赋初值0,而带参数的构造函数,即构造函数2将两个数据成员设置为形参值。

2.调用构造函数

当定义对象时,构造函数会自动执行。因为一个类可能会有包括默认函数在内的不止一种构造函数。

调用默认构造函数的语法:Myclass2 sa;将sa定义为Myclass2类型的对象,在这种情况下,会默认执行构造函数,从而将sa的数据初始化为0。因此在程序中定义一个对象而没有指明初始化时,编辑器便按照默认构造函数来初始化对象。

如果一个类没有定义构造函数,编辑器会自动生成一个不带参数的默认构造函数,格式如下:

类名::默认构造函数名()

{

}

假设一个类中包含有带参数的构造函数,调用这种带参数的构造函数语法例子:Myclass2 sal(10,20);

其中的参数可以是变量,也可以是表达式,常数。该语句定义了一个Myclass2类型的对象sal。会执行Myclass2类中带参数的构造函数,从而将数据成员x和y分别赋值为10和20。

定义变量时除了可以用=外还可以像定义对象一样调用其构造函数给变量赋值例如:

int a(10);//等价于int n=10;
char a[]("asd");//等价于char a[]="asd";

3.用new动态创建对象

new是C++中用于动态内存分配的运算符,在C语言中一般使用malloc函数。在new后面写上一个类型,会在内存中分配空间,并自动调用这个类型的构造函数,然后返回一个指向这块内存的指针,这样就完成了新建一个此类型变量。

#include<iostream.h>
class TPoint1
{
    int x,y;
    public:
    TPoint1(int x1,int y1);   //构造函数
    {
        x=x1;y=y1;
    }
    void dispoint()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
};
void main()
{
    TPoint1 a(12,6), *p=new TPoint1(5,12);//调用构造函数,p指向一个匿名的对象
    a.dispoint();
    p->dispoint();
}

输出的结果为(12,6)和(5,12)。

#include<iostream.h>
class Myclass3
{
    int x,y;
    public:
    Myclass3()
    {
        x=y=0;
    }
    Myclass3(int a,int b)
    {
        x=a;y=b;
    }
    void disp()
    {
        cout<<"x="<<x<<",y="<<y<<endl;
    }
};
void main()
{
    Myclass3 s(2,3),*p=&s;
    p->disp();
}

通过对象指针来调用对象成员函数,运行结果为x=2,y=3。

程序执行到定义对象语句时系统会为对象分配内存空间,系统自动调用构造函数,将实参传给形参,执行构造函数时,将形参赋给对象的数据成员,完成数据成员的初始化工作。

4.构造函数的重载

在一个类中可以定义多个构造函数,以便为对象提供不同的初始化的方法,供用户选用,这些构造函数具有相同的名字,而参数的个数或参数的类型不相同,这称为构造函数的重载。

一个类的构造函数之间可以相互调用。当一个构造函数调用另一个构造函数时,可以使用关键字this,同时这个调用语句应该是整个构造函数的第一个可执行语句。

class TPoint2
{
    int x,y;
    public:
        TPoint2(){ }   //默认构造函数
        TPoint2(int x1,int y1)//重载构造函数
        {
            x=x1;y=y1;
        }
    void set(int x1,int y1)
    {
        x=x1;y=y1;
    }
    void dispoint()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
};
void main()
{
    TPoint2 a(12,6),b;
    a.dispoint();
    b.set(5,18);
    b.dispoint();
}

两个构造函数,一个有参数一个不带参数。定义a对象自动调用第二个构造函数,b自动调用第一个构造函数,这就是构造函数的重载。

5.复制构造函数

复制构造函数的参数可以是 const 引用,也可以是非 const 引用。 一般使用前者,这样既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数去初始化其他对象。默认构造函数(即无参构造函数)不一定存在,但是复制构造函数总是会存在。

在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”):

  1. 一个对象作为函数参数,以值传递的方式传入函数体;

  2. 一个对象作为函数返回值,以值传递的方式从函数返回;

  3. 一个对象用于给另外一个对象进行初始化(常称为赋值初始化);

如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是拷贝构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。

类名(形参表列); //普通构造函数的声明,如Box(int h ,int en);

类名(类名& 对象名); //复制构造函数的声明,如Box(Box &b);

普通构造函数在程序中建立对象时被调用。 复制构造函数在用已有对象复制一个新对象时被调用。

class TPoint4
{
    int x,y;
    public:
    TPoint4(int x1,int y1)  //构造函数
    {
        x=x1;y=y1;
    }
    TPoint4(const TPoint4 &obj)//复制构造函数
    {
        x=obj.x;y=obj.y;
    }
    void disopint()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
};
void main()
{
    TPoint4 a(12,16),b(a);
    a.dispoint();
    b.dispoint();
}

运行结果为(12,16)(12,16)。

析构函数

c++程序设计原则是由系统自动分配的内存空间由系统自动释放。手工分配的内存空间必须手工释放,否则可能会造成内存泄漏。

解决问题的方法是使用析构函数。对象消亡时,自动被调用,用来释放对象占用的空间。

1.析构函数性质

一个类只有一个析构函数,而且析构函数没有参数

析构函数的格式是~加上类的名字(中间没有空格)

与构造函数一样没有任何类型,不能像其他函数那样被调用

析构函数在类对象销毁时自动执行

2.析构函数调用

class Rectange1
{
    private:
    int x1,y1,x2,y2;//定义矩形的坐标变量
    public:
    Rectange1(int a1,int b1,int a2,int b2)//定义带参的构造函数
    {
        cout<<"调用带参构造函数"<<endl;
        x1=a1;y1=b1;x2=a2;y2=b2;
    }
    Rectange1()//定义了无参的构造函数
    {
        cout<<"调用了无参函数"<<endl;
        x1=0;x2=0;y1=0;y2=0;
    }
    ~Rectange1()
    {
        cout<<"调用了析构函数"<<endl;
    }
    int Area()
    {
        return (x2-x1)*(y2-y1);
     }
};
    void main()
    {
        Rectange1 r1(1,3,8,12)
            cout<<"矩形r1的面积="<<r1.Area()<<endl;
        Rectange1 r2;
        cout<<"矩形r2的面积="<<r2.Area()<<ebdl;
        
    }

本程序运行结果如下:

调用了带参构造函数

矩形r1的面积=63

调用了无参构造函数

矩形r2面积=0

调用了析构函数

调用了析构函数

在程序执行过程中,对象r1、r2在主函数结束遇到后括号时,自动调用析构函数,因此输出结果有两个析构函数

3.用new 动态创建对象时调用析构函数

这种情况只有用delete释放对象时,才调用析构函数。若不使用delete撤销,程序结束时对象仍然存在,并占用相应的内存空间,即系统不能自动撤销动态创建的对象。

class TPoint5
{
    int x,y;
    public:
    TPoint5(int x1,int y1)//构造函数
    {
        x=x1;y=y1;
    }
    ~TPoint5()
    {
        cout<<"调用了析构函数"<<endl;
    }
    void dispoint()
    {
        cout<<"("<<x<<","<<y<<")"<<endl;
    }
};
void main()
{
    TPoint5 a(12,6),*p=new TPoint5(5,12);//对象指针指向创建的匿名对象
    a.dispoint();
    p->dispoint();
    delete p;
}

程序运行结果如下:

(12,6)

(5,12)

调用了析构函数

调用了析构函数

对于类对象,系统会自动调用析构函数。对于new运算符创建的对象,必须在使用delete运算符后才调用析构函数。

一个对象的生存期

给对象分配内存空间——>由构造函数初始化——>调用它的成员函数——>由析构函数清除——>销毁内部对象——>从内存中清除该对象。

静态成员

是类中所有对象的共享成员,有时称为类变量。

使用方法:定义之前要加上static关键字

初始化格式:类型 类名::静态数据成员=值;

引用格式:类名::静态数据成员

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值