当派生类中含有对象成员时,构造函数调用顺序:基类构造函数→对象成员的构造函数→子类构造函数,析构函数则相反
1.类的构造函数(初始化函数)
所谓初始化,就是把一个刚创建的数据设置成我想要的值,而不是一个不能掌控的随机数。
如果初始化用的是一般的赋值,当属性很多且而大部分属性都是默认的情况下,这种方法会麻烦。
c++为类提供了初始化函数,这个函数在对象被创建时有系统自动调用,是创建对象的最后一步。也就是说,初始化函数是创建对象的一部分,初始化函数退出之后,该对象才完成了创建。初始化函数和类名要保持一致,且没有返回值,也没有void
class Cat
{
public: //类的权限系统,后面再说,现在暂时都用public
char name[100];
int age;
char sex;
public:
Cat(char* name,int age,char sex) //初始化函数
{
strcpy(this->name,name);
this->age=age;
this->sex=sex;
}
void printInfo()
{
cout<<"This cat's name is "<<name<<endl;
cout<<"This cat's age is "<<age<<endl;
}
};
//而创建对象Cat myCat;是错误的!!
scanf("%s %d %c",name,age,sex);
Cat myCat(name,age,sex);
构造函数后面加上冒号: 作用是冒号后面跟着的是初始化列表。
构造函数列表初始化执行顺序与成员变量在类中声明顺序相同,与初始化列表中语句书写先后无关。
相对于在构造函数中赋值,初始化列表执行效率更高。
A( int aa, int bb ):a(aa),b(bb)
{
}
相当于
A( int aa, int bb )
{
a=aa;
b=bb;
}
private:
int a,b;
(1)对含有对象成员的对象进行初始化
调用格式为“构造函数 : A(初始值),B(初始值),C(初始值)……”
类line有两个私有对象成员startpoint、endpoint,line的构造函数写成:
line(int sx,int sy,int ex,int ey):startpoint(sx,sy),endpoint(ex,ey){……}
初始化时按照类定义中对象成员的顺序分别调用各自对象的构造函数,再执行自己的构造函数
class rectangle //头文件中类定义
{
public:
rectangle( int pointX, int pointY, int Width, int Length );
private:
CPoint m_point;
int m_Width;
int m_Length;
};
rectangle::rectangle(int pointX, int pointY, int Width, int Length) :
m_point(pointX,pointY),m_Width(Width),m_Length(Length)//源文件中构造函数实现
{
...
}
等价于
rectangle::rectangle(int pointX, int pointY, int Width, int Length)//源文件中构造函数实现
{
m_point.X = pointX;
m_point.Y = pointY;
m_Width = Width;
m_Length = Length;
...
}
(2)对于不含对象成员的对象
类rectangle有两个数据成员length、width,其构造函数写成:
rectangle():length(1),width(2){}
rectangle(int x,int y):length(x),width(y){}
(3)对父类进行初始化
调用格式为“子类构造函数 : 父类构造函数”,如下,其中QMainWindow是MyWindow的父类:
MyWindow::MyWindow(QWidget* parent , Qt::WindowFlags flag) : QMainWindow(parent,flag)
(4)对类的const成员变量进行初始化
由于const成员变量的值无法在构造函数内部初始化,因此只能在变量定义时赋值或使用初始化列表赋值。
2.析构函数
析构函数是对象被销毁时,系统最后调用的一个函数,一般用于扫尾工作。析构函数和构造函数一样,没有的话,系统有默认的,也可以自己定义。析构函数没有返回值,且不带参数。
class Node
{
public:
int index;
Node* left;
Node* right;
public:
Node(int i) //构造函数,带一个参数
{
index=i;
left=0;
right=0;
}
~Node() //析构函数
{
if (left)
delete left;
if (right)
delete right;
}
一般成员变量里有指针时要写,因为这些指针指的空间一般是用new运算符分配的,不会随着对象的销毁而释放,因为系统只会自己释放那个指针用来存地址的空间,它指的那个空间很可能就孤立了,别人也再也指不过去了,所以要手动释放。
#include<iostream>
using namespace std;
class B
{
int x,y;
public:
B() {x=y=0;cout<<"Constructor1"<<endl;}
B(int i) {x=i;y=0;cout<<"Constructor2"<<endl;}
B(int i,int j) {x=i;y=j;cout<<"Constructor3"<<endl;}
~B() {cout<<"Destructor"<<endl;}
void printf()
{
cout<<"x="<<x<<",y="<<y<<endl;
}
};
int main()
{
B *ptr;
ptr=new B[3]; //调用第一个构造函数3次
ptr[0]=B(); //创建一个无名对象,等价于{B temp; ptr[0]=temp; 释放temp},因此调用第一个构造函数1次,调用析构函数1次;
ptr[1]=B(5);
ptr[2]=B(2,3);
for(int i=0;i<3;i++)
ptr[i].printf();
delete[] ptr; //删除对象数组ptr,调用析构函数3次
}
/*
Constructor1
Constructor1
Constructor1
Constructor1
Destructor
Constructor2
Destructor
Constructor3
Destructor
x=0,y=0
x=5,y=0
x=2,y=3
Destructor
Destructor
Destructor
*/