一、构造函数:
构造函数和析构函数是在类体中说明的两种特殊的成员函数。构造函数是在创建对象时,使用给定的值来将对象初始化。
析构函数的功能正好相反,是在系统释放对象前,对对象做一些善后工作。构造函数可以带参数、可以重载,同时没有返回值。
构造函数是类的成员函数,系统约定构造函数名必须与类名相同。构造函数提供了初始化对象的一种简单的方法。
例:
#include <iostream>
using namespace std;
class Test //类
{
int x, y;
public:
Test(int a, int b)//构造函数,带参数
{
x = a;
y = b;
cout << " x=" << x << ", y=" << y << endl;
}
int sum_xy()
{
return x + y;
}
};
int main()
{
Test test1(56,70);//只要程序运行,系统自动会初始化构造函数(自己会匹配找到带有两个参数的构造函数)
cout << " x+y=" << test1.sum_xy() << endl;
return 0;
}
输出结果:
【对构造函数,说明以下几点】:
1.构造函数的函数名必须与类名相同。构造函数的主要作用是完成初始化对象的数据成员以及其它的初始化工作。
2. 在定义构造函数时,不能指定函数返回值的类型,也不能指定为void类型。
3. 一个类可以定义若干个构造函数。当定义多个构造函数时,必须满足函数重载的原则。
4.构造函数可以指定参数的缺省值。
5.若定义的类要说明该类的对象时,构造函数必须是公有的成员函数。如果定义的类仅用于派生其它类时,则可将构造函数定义为保护的成员函数。
由于构造函数属于类的成员函数,它对私有数据成员、保护的数据成员和公有的数据成员均能进行初始化。
例:
#include <iostream>
using namespace std;
class Test //类
{
int x, y;
public:
Test(int a, int b)//构造函数,带参数
{
x = a;
y = b;
cout << " x=" << x << ", y=" << y << endl;
}
Test(int a)//满足函数重载
{
cout << " a=" << a << endl;
}
};
int main()
{
Test test1(56,70);//只要程序运行,系统自动会初始化构造函数(自己会匹配找到带有两个参数的构造函数)
Test test2(3000);//
return 0;
}
输出结果:
对局部对象,静态对象,全局对象的初始化:
1)对于局部对象,每次定义对象时,都要调用构造函数。
2)对于静态对象,是在首次定义对象时,调用构造函数的,且由于对象一直存在,只调用一次构造函数。
3)对于全局对象,是在main函数执行之前调用构造函数的。
例:
#include <iostream>
using namespace std;
class Test //类
{
int x, y;
public:
Test(int a, int b)//构造函数,带参数
{
x = a;
y = b;
cout << " 初始化局部对象" << endl;
}
Test(int a)
{
x = a;
y = 0;
cout << " 初始化全局对象" << endl;
}
Test()
{
x = 0;
y = 0;
cout << " 初始化静态局部对象" << endl;
}
};
Test test2(3000);//定义全局对象
void FuncTest()
{
cout << " 程序开始进入FuncTest()函数" << endl;
Test test3(15, 20);//定义局部对象
static Test test4;//定义局部静态变量 // 尾部 不能加()
}
int main()
{
cout << " 程序开始执行 --> main()函数" << endl;
Test test1(56,70);//定义局部对象
FuncTest();
return 0;
}
输出结果:
【缺省的构造函数】
在定义类时,若没有定义类的构造函数,则编译器自动产生一个缺省的构造函数,
其格式为: className::className() { } 缺省的构造函数并不对所产生对象的数据成员赋初值;即新产生对象的数据成员的值是不确定的。
【关于缺省的构造函数】,说明以下几点:
1、在定义类时,只要显式定义了一个类的构造函数,则编译器就不产生缺省的构造函数。
2、所有的对象在定义时,必须调用构造函数 不存在没有构造函数的对象!
3、在类中,若定义了没有参数的构造函数,或各参数均有缺省值的构造函数也称为缺省的构造函数,缺省的构造函数只能有一个。
4、产生对象时,系统必定要调用构造函数。所以任一对象的构造函数必须唯一。
【构造函数与new运算符】
可以使用new运算符来动态地建立对象。建立时要自动调用构造函数,以便完成初始化对象的数据成员。最后返回这个动态对象的起始地址。
用new运算符产生的动态对象,在不再使用这种对象时,必须用delete运算符来释放对象所占用的存储空间。
用new建立类的对象时,可以使用参数初始化动态空间。
例:
#include <iostream>
using namespace std;
class Test //类
{
int x, y;
public:
Test(int a, int b)//带参数的构造函数
{
x = a;
y = b;
cout << " 调用带参构造函数" << endl;
}
Test()//缺省的构造函数
{
x = 0;
y = 0;
cout << " 调用缺省构造函数" << endl;
}
void disp_xy()
{
cout << " x=" << x << ", y=" << y << endl ;
}
};
int main()
{
Test *pt1, *pt2;//创建两个对象指针
pt1 = new Test; //用new动态开辟存储空间,调用缺省构造函数
pt2 = new Test(200, 800);//用new动态开辟存储空间,调用带参构造函数
pt1->disp_xy();
pt2->disp_xy();
delete pt1;//用delete释放动态开辟的存储空间
delete pt2;
return 0;
}
输出结果:
二、析构函数:
析构函数的作用与构造函数正好相反,是在对象的生命期结束时,释放系统为对象所分配的空间,即要撤消一个对象。
析构函数也是类的成员函数,定义析构函数的格式为:
ClassName::~ClassName( )
{
......
// 函数体;
}
例:
#include <iostream>
using namespace std;
class Test //类
{
double x, y;
public:
Test(double a, double b)//带参数的构造函数
{
x = a;
y = b;
cout << " 调用带参的构造函数" << endl;
}
Test()//缺省的构造函数
{
x = 0;
y = 0;
cout << " 调用缺省的构造函数" << endl;
}
~Test()//析构函数
{
cout << " 调用析构函数" << endl;
}
};
int main()
{
Test pt1;//创建三个对象
Test pt2(5.6,70.8);
Test pt3(10, 20);
return 0;
}
输出结果:
【析构函数的特点如下】:
1、析构函数是成员函数,函数体可写在类体内,也可写在类体外。
2、析构函数是一个特殊的成员函数,函数名必须与类名相同,并在其前面加上字符“~”,以便和构造函数名相区别。
3、析构函数不能带有任何参数,不能有返回值,不指定函数类型。
4、一个类中,只能定义一个析构函数,析构函数不允许重载。
5、析构函数是在撤消对象时由系统自动调用的。
在程序的执行过程中,当遇到某一对象的生存期结束时,系统自动调用析构函数,然后再收回为对象分配的存储空间。
在程序的执行过程中,对象如果用new运算符开辟了空间,则在类中应该定义一个析构函数,并在析构函数中使用delete删除由new分配的内存空间。因为在撤消对象时,系统自动收回为对象所分配的存储空间,而不能自动收回由new分配的动态存储空间。
【缺省的析构函数】
若在类的定义中没有显式地定义析构函数时,则编译器自动地产生一个缺省的析构函数,其格式为: ClassName::~ClassName() { }; 任何对象都必须有构造函数和析构函数,但在撤消对象时,要释放对象的数据成员用new运算符分配的动态空间时,必须显式地定义析构函数。
例:
#include <iostream>
using namespace std;
class Test //类
{
int *px;
public:
Test()//缺省的构造函数
{
cout << " 调用构造函数Test::Test()" << endl;
px = new int(1000);
cout << " *px=" << *px << endl;
}
~Test()//析构函数
{
delete px;
cout << " 调用析构函数~Test::Test()" << endl;
}
};
int main()
{
//Test pt1;//创建对象,用完释放对象
Test *pt1 = new Test;
Test* pt2 = (Test*)malloc(sizeof(Test));
delete pt1; //动态释放 归还堆的空间
free(pt2);
return 0;
}
输出结果:
【实现类型转换的构造函数】
同类型的对象可以相互赋值,相当于类中的数据成员相互赋值;
如果直接将数据赋给对象,所赋入的数据需要强制类型转换,这种转换需要调用构造函数。
例:
#include <iostream>
using namespace std;
class Test //类
{
double x,y;
public:
Test(double a,double b)//构造函数
{
x = a;
y = b;
cout << " 调用带参的构造函数" << endl;
}
~Test()//析构函数
{
cout << " 调用析构函数" << endl;
}
void disp()
{
cout << " x=" << x << ", y=" << y << endl;
}
};
int main()
{
Test test1(2.4, 4.75);//创建对象,赋初值
test1.disp();
test1 = Test(90.7, 40);//重新给对象赋值,要数据类型转换
test1.disp();
return 0;
}
输出结果:
三、拷贝构造函数
拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。
拷贝构造函数通常用于:
1)通过使用另一个同类型的对象来初始化新创建的对象;
2)复制对象把它作为参数传递给函数;
3)复制对象,并从函数返回这个对象。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。
拷贝构造函数的最常见形式如下:
classname (const classname &obj)
{
......
// 构造函数的主体
}
例:
#include <iostream>
using namespace std;
class Test //类
{
int* px;
public:
Test(int a);//构造函数声明
Test(const Test& test);//拷贝构造函数声明
~Test();//析构函数声明
int get_px()
{
return *px;
}
};
Test::Test(int a)//构造函数
{
cout << " 调用构造函数" << endl;
px = new int; //为指针分配内存空间
*px = a;
}
Test::Test(const Test& test)//拷贝构造函数
{
cout << " 调用拷贝构造函数 并为指针px 分配内存空间" << endl;
px = new int;
*px = *test.px; //拷贝值
}
Test::~Test()//析构函数定义
{
cout << " 调用析造函数,释放动态分配的内存空间" << endl;
delete px;
}
void dis_px(Test test)
{
cout << " *px =" << test.get_px() << endl;
}
int main()
{
Test test1(3000);//创建对象
dis_px(test1); //拷贝
return 0;
}
输出结果: