- 没有返回值说明,定义时不能指出函数返回值的类型,连void都不能有。
- 不能被继承
- 构造函数可以有默认参数,析构函数没有参数
- 析构函数可以是virtual的,构造函数不能
- 不可取他们的地址
- 不能用常会方法调用构造函数。当使用完全限定名(带对象名、类名和函数名)可以调用析构函数。
- 定义对象,自动调用构造函数;删除对象,自动调用析构函数。
2. 类构造函数:专门用于构造新对象,将值赋予它们的数据成员。
构造函数是一种特殊的类成员函数,在创建类对象时被调用。构造函数的名称和类名相同,但通过函数重载,可以创建多个重名的构造函数,条件是每个函数的参数列表都不同。
(1) 默认构造函数
每个类必须有一个构造函数,若未显示定义一个类的构造函数,则C++提供一个默认构造函数,该函数是个无参构造函数,仅负责创建对象,不做任何初始化工作。
如果类定义了一个带参数的构造函数,如果想使用无参构造函数,必须自己定义。
(2) 对象数组
若要说明一个带有构造函数的类的对象数组,这个类一般情况下会含有一个不带参数的构造函数或带有默认构造函数的构造函数。
因此为这个对象数组的每个元素调用了一次缺省构造函数来初始化每个元素对象。
(3) 拷贝构造函数
(3.1)拷贝构造函数的调用
拷贝构造函数的功能是用一个已有的对象来初始化一个被创建的同类对象,是一种特殊的构造函数。
其形参是本类对象的引用,它的特殊功能是将参数代表的对象逐域拷贝到新创建的对象中。
如果用户没有声明类的拷贝构造函数,系统会自动生成一个默认拷贝构造函数,这个默认构造函数的功能是把初始对象的每个数据成员的值都复制到新建立的对象中。
声明形式:
类名(类名 & 对象名)
class cat
{
private:
int age;
float weight;
char * color;
public:
cat();
cat(cat&);
void play();
void hunt();
};
cat::cat(cat &other)
{
age=other.age;
weight=other.weight;
color=other.color;
}
在下面四中情况系统会自动调用拷贝构造函数:
- 用类的一个对象去初始化另一个对象
cat cat1;
cat cat2(cat1);
- 用类的一个对象去初始化另一个对象时的另外一种形式
cat cat2(cat1);
- 对象作为函数参数传递时,调用拷贝构造函数
f(cat a){}
cat b;
f(b); //调用拷贝构造函数
- 如果函数的返回值是类的对象,函数调用返回时,调用拷贝构造函数
cat f()
{
cat a;
.....
return a;
}
cat b;
b=f();
(3.2) 深拷贝构造函数和浅拷贝构造函数
由C++语言提供的默认拷贝构造函数只是对对象进行浅拷贝(逐个成员依次拷贝),即支付至对象空间而不复制资源。
如果对象的数据成员包括指向堆空间的指针,因为两个对象都拥有同一个资源,对象析构时,该资源将经历两次资源返还,就不能使用这种拷贝构造函数,必须自定义深拷贝构造函数。
例子(暂空)
同时满足两个条件,必须定义深拷贝构造函数:
- 肯定要调用拷贝构造函数。
- 数据成员包括指向堆内存的指针变量。
#include<string>
#include<iosteam>
using namespace std;
class Person
{
public:
Person(char * na)
{
cout<<"call constructor"<<endl;
name=new char[strlen(na)+1];
if(name!=0)
{strcpy(name,na);}
}
Person(Person&p)
{
cout<<"call copy constructor"<<endl;
name =new char[strlen(p.name)+1];
if(name!=0)
{strcpy(name,p.name);}
}
void printname()
{
cout<<name<<endl;
}
private:
char *name;
};
void main()
{
Person wang("wang");
Person li(wang);
wang.printname();
li.printname();
}
程序运行结果:
call constructor
call copy constructor
wang
wang
当有使用new动态分配内存空间的数据成员,在析构函数中使用delete进行动态内存空间的释放以及对赋值=运算符进行重载,应该自定义深拷贝构造函数。
(4)数据成员初始化
构造函数可以采用以下形式对数据成员初始化
4.1 在构造函数的函数体中初始化
Circle::Cirlce(float r)
{radius=r;}
4.2使用构造函数初始化表对数据成员进行初始化
Circle::Circle(float r):radius(r)
{}
注:数据成员的初始化的次序取决于它们在类定义中的声明次序,与它们在成员初始化表中的次序无关。
常量和引用的初始化必须放在构造函数正在建立数据成员结构的时候,也就是必须用4.2方式初始化。
4.3 混合初始化
Student::Student(int n,int a,char * name):number(n),age(a)
{strcpy(name,pname);}
4.4 使用拷贝构造函数初始化
(5) 类类型和基本数据类型的转换
类型转化就是将一种类型的值转换为另一种类型的值。一般数据类型之间的转换分为隐式类型转换和显示类型转换。
类和基本数据类型之间的转换可以通过以下方法进行:
5.1 构造函数用作类型转换(基本数据类型->类类型)
3. 当对象被删除时,程序将调用析构函数。每个类都只能有一个析构函数。析构函数没有参数,其名称为类名称前加上~。
如果构造函数使用了new,那必须提供使用delete的析构函数。
(1)析构函数被自动调用的三种情况
- 一个动态分配的对象被删除,即使用delete删除对象时,编译系统会自动调用析构函数
- 程序运行结束时
- 一个编译器生成的临时对象不再需要时
(2)析构函数的手工调用
除对象数组之外,构造函数只能由系统自动调用,而析构函数可以使用下述方法手工调用。
对象名.类名::析构函数名();
(3)析构函数与构造函数的调用顺序
构造函数和析构函数的调用顺序刚好相反,限构造后析构
#include<iostream.h>
#include<string.h>
class Student{
public:
Student(char* pName="no name",int ssId=0)
{
strncpy(name,pName,40);
name[39]='\0';
id = ssId;
cout<<"Constructing new student"<<pName<<endl;
}
Student(Student& s)
{
cout<<"Constructing copy of"<<s.name<<endl;
strcpy(name,"copy of");
strcat(name,s.name);
id=s.id;
}
~Student()
{
cout<<"Destructing"<<name<<endl;
}
proctected:
char name[40];
int id;
};
void fn(Student s)
{
cout<<"In function fn()\n"; //fn调用结束,析构对象s
}
void main()
{
Student randy("Randy",1234); //调用构造函数,创建对象randy
Student wang("wang",5678); //调用构造函数,创建对象wang
cout<<"Calling fn()\n";
fn(randy); //调用fn函数,参数传递时调用拷贝构造函数
cout<<"Return from fn()\n"; //主函数调用结束时,先析构对象wang,再析构对象randy
}
//程序运行结果:
Constructing new student Randy
Constructing new student wang
Calling fn()
Constructing copy of Randy
In function fn()
Destructing copy of Randy
Return from fn()
Destructing wang
Destructing Randy