构造函数与析构函数

来源:互联网 发布:程序员工具 编辑:程序博客网 时间:2024/05/20 05:09

概述:一个类有两种特殊的成员函数:构造函数与析构函数。构造函数功能是在创建对象时,给数据成员赋初值,即对象的初始化。析构函数的功能是释放一个对象,在对象删除前,用它做一些内存释放工作,与构造函数的功能相反。


构造函数:在对象建立时它会被自动执行,因此变量、对象的初始化代码一般都放在构造函数中。

  (1)对象的初始化过程

先看程序中变量、数组、结构变量和对象的初始化过程。

在C++中,变量初始化有以下两种等价形式:

  int a(5);      //C++初始化形式

  int b=8;      //C初始化形式

    定义时初始化的目的是在编译时为变量开辟内存空间的同时将初始值写入内存空间。

数组初始化:只有 ” ={处置列表} “ 这种形式。例如下列代码

   double c[] = { 1,2,3,4};

等价于下列赋值语句

  c[[0]=1; c[1]=2; c[3]=3; c[4]=4;

        结构变量初始化: 也使用 ” ={初值列表} “这种形式,代码如下:

struct  PERSON

{

char   name[20];    //姓名

float   height;        //身高

float   weight;       //体重

};

PERSON one={"DONG", 170, 130};

等价于下列赋值语句

strcpy(one.name, "DONG");   one.height=170;  one.weight=130;

struct中各成员的默认访问权限是public,因此上述初始化操作是合法的。类的对象有所不同

类对象初始化:

class  CPerson

{

char  name[20];     //姓名

float  height;          //身高

float  weight;         //体重

}

CPerson  one={ "DONG", 170, 130};  //错误的赋值方式,

  等价于赋值方式: strcpy(one.name, "DONG");  one.height=170;  one.weight=130;

  因为类中的数据成员默认都是private的,无法通过对象one来访问私有数据成员。可以将class各成员访问权限             设置为public,这样效果等同于struct,却降低了类的封装性。

类对象的数据成员初始化是通过类的成员函数来完成的

 class  CPerson

{

public:

void setValue(char *str, float h, float w)

{

strcpy(name,str);  height = h;  weight=w;

}

private:

char  name[20]; 

float  height;

float  weight;

};

于是:CPerson  one;     one.setValue("DONG", 170, 130);  // A: 调用成员函数设置初始值

A 语句通过调用成员函数来设定对象one的数据成员初始值,并非是对象的初始化格式。

一个对象的初始化是指在定义的同时赋初值.

       一个类可以看成是一个整体类型,类似于C++内建的基本类型int、char、float、double等。

对象初始化可以有:CPerson  one( "DONG", 170, 130);

对象初始化不一定要对所有的数据成员进行初始化,还可以有:

CPerson  one("DONG", 170, 130);

CPerson  one(170, 130);

CPerson  one;

为了实现以上形式的对象初始化操作,需要使用类的构造函数(Constructor)。


(2)构造函数

C++规定,一个类的构造函数必须与类同名,它可带参数也可不带参数,可以重载,也可以有默认的形参值。

上述对象初始化形式就是对象相应版本对构造函数的调用。例如:

one.CPerson("DONG", 170, 130);

one.CPerson(170, 130);

one.CPerson();

1、构造函数名必须与类名相同 2、定义的构造函数不能指定其返回值类型,也不能指定为void类型,构造函数主要是用于对象数据成员初始化,因而无须返回函数值,也就无须又返回类型。3、若要定义类对象,构造函数必须是公有型成员函数,否则,类无法实例化。若类仅用于派生其他类,则构造函数可定义为保护型成员函数。

        4、构造函数重载或设定构造函数默认形参值时,避免二义性。例如:

CPerson(char *str, float h=170, float w=130) // A

{

strcpy(name, str);  height=h;  weight=w;

        }

CPerson(char *str)   // B

{

strcpy(name, str);

}

     当 CPerson  other(" DONG "); 时,即 other.CPerson("DONG");此时无法确认是哪个上述构造函数的调用。


默认构造函数:

CPerson()

{

}

目的是使下列对象的定义形式合法:CPerson  one;

若类中定义了指定的构造函数,则隐式的默认构造函数不再存在,对于 CPerson  one 会报错。

在类的设计中,无论声明的类有几个重载的构造函数,最好有一个显式定义的默认构造函数。这样可以保证一个         类总会有一个默认的构造函数,或者是显式的,或者是隐式的。


(3)析构函数

形如:

~CPerson()

{

}

1、每个类最多有一个析构函数,且应为 public 型,否则类实例化后无法自动调用析构函数进行释放。

2、不能被重载、没有任何参数、没有返回值、函数名前不能有任何关键字。例如void

3、析构函数也可以在类体外定义,但必须指明它所属的类,在类体中还必须有析构函数的声明。

4、如果类的声明中没有定义析构函数,编译时会自动生成一个隐式的不进行任何操作的默认析构函数。


在析构函数中,当要释放的指针指向动态内存时,一定要先判断指针的指向是否有效,且内存释放后,一定要将指针置为0,NULL或null。这样当析构函数多次调用时,由于有判断语句,就不出现 ”释放未知内存的误“发生。



0 0