天天看点

c++的构造函数和析构函数

构造函数的需求

类内部的私有成员和保护成员在外界是不可以被访问的,这也正是类的封装性的体现,所以在要为类的成员变量初始化时,这个任务就落在了类的成员函数。

class A
{
   private:
      int a;
      int b;
   public:
      void init(int a,int b)
      {
         this->a=a;
         this->b=b;
       }
};           

通过上述代码,我们是可以通过调用init函数对类的成员变量进行赋值,这意味着当我们创建一个对象,都要手动的调用init来初始化对象,这样的类机制是不理想的,于是就想到在创建对象时,程序自动调用构造函数进行初始化。(析构函数同样是程序自动调用的,它也是出于考虑到上述的原因)。另外考虑到类的唯一性和对象的多样性,于是就打算用类名作为构造函数的函数名。(析构函数是使用~加类名作为函数名)。

构造函数的使用

#include<iostream>
using namespace std;

class A
{
private:
    int a;
    int b;
public:
    A(int a, int b)         //类的构造函数,函数名必须和类名相同。
    {
        this->a = a;     //构造函数和普通成员函数一样,它会有一个默认的参数,指向对象本身,就是this指针
        this->b = b;
    }
    void print()        //输出函数
    {
        cout << a << endl;
        cout << b << endl;
    }
};

int main()
{
    A a(10, 11);
    a.print();
    system("pause");
    return 0;
}           

输出结果:

c++的构造函数和析构函数

由上述的例子我们可以发现,构造函数是程序自己调用的,所以我们就完成了对a和b的初始化。

(构造函数是没有返回值的,也不允许有,但可以有无值的return。)

析构函数

析构函数也是特殊的函数,没有返回值,没有参数,不能随意调用,没有重载。只是在类对象生命周期结束的时候自动调用。其实我们把析构函数称为“逆构造函数”,在构造函数名前加上运算符“~”。析构函数的作用就是释放对象的所使用的内存资源。析构函数的调用顺序和构造函数的调用顺序相反。

eg:

#include<iostream>
using namespace std;

class Student
{
public :
    Student()
    {
        cout << "I'm a student" << endl;
    }
    ~Student()
    {
        cout << "I'm a student 的析构" << endl;
    }
};

class Teacher
{
public:
    Teacher()
    {
        cout << "I'm a teacher" << endl;
    }
    ~Teacher()
    {
        cout << "I'm a teacher 的析构" << endl;
    }
};

class School
{
private:
    /*
    此处以其他类的对象作为成员变量
    */
    Student s;
    Teacher t;
public:
    School()
    {
        cout << "I'm a school" << endl;
    }
    ~School()
    {
        cout << "I'm a school 的析构" << endl;
    }
};

int main()
{
    School();
    system("pause");
    return 0;
}           
c++的构造函数和析构函数

如上述代码所示,当一个类把其他类的对象作为成员变量时候,当该类创建对象自动调用该类的构造函数时,它也会同时自动调用其成员变量中类对象的构造函数,此时的构造函数调用顺序为:先调用成员变量中的类对象的构造函数,按照声明的先后顺序调用,最后调用自己的构造函数。而析构函数的调用就和构造函数的调用顺序相反。于是我们就可以得到如图的输出结果。

重载构造函数

首先我们解释一下重载的含义就是函数或方法有相同的名称,但是参数列表不同的情形。构造函数作为函数的一种,当然也可以被重载。下面给出函数重载的样例,(string就是一个类,其中就是通过构造函数的重载完成对string对象多种初始化方式)

class A
{
private:
    int a;
    int b;
public:
    /*
    构造函数的重载,定义对象时,只是通过参数的个数决定使用哪个构造函数
    */
    A();
    A(int a);
    A(int a, int b);
};           

重载构造函数为类提供了多种初始化方式,在定义对象时,程序根据所提供的参数个数来决定调用哪个构造函数。

默认的构造函数

(1):c++规定每个类必须要有一个构造函数,没有构造函数,就无法创建对象

(2):如果没定义构造函数,c++会自动的为提供一个默认的无参构造函数,但是该构造函数什么也没有干,只是拿来创建对象的。

(3):只要为一个类定义了一个构造函数(无论是有参还是无参的),系统都不会再自动的为类提供构造函数,如果需要使用无参构造函数,那就需要自己定义一个无参构造函数。

(4):创建对象和定义普通变量是一样的,在用默认的构造函数创建对象时,如果创建的是全局对象或静态对象,则对象的位模式为全0,否则,对象是随机赋值的。

类成员初始化问题

在一个类中,如果有用户自定的类对象做为其成员(如前面讲析构函数和构造函数调用顺序),那么构造函数是如何工作的了?

(1):定义该类时只需要使用无参构造函数时

#include<iostream>
using namespace std;

class Student
{
public :
    Student()
    {
        cout << "Student" << endl;

    }
};
class School
{
private:
    Student s;        //定义了一个类成员
public:
    School()
    {
        cout << "School" << endl;     //在调用School的构造函数之前会先调用Student的构造函数
    }
};

int main()
{
    School s;
    system("pause");
    return 0;
}           
c++的构造函数和析构函数

从输出结果可以看出,当我们创建了一个school对象时,它会调用构造函数来初始化其成员,其中它的一个类成员–Student s,也将调用它自己构造函数来初始化自己。

(2):当需要使用自定义构造函数(含参数)来初始化对象时

#include<iostream>
using namespace std;

class Student
{
private:
    int a;
public :
    Student(int a)
    {
        this->a = a;
        cout << "Student a="<<this->a << endl;

    }
};
class School
{
private:
    Student s;        //定义了一个类成员
public:
    School(int a):s(a)                 //这是初始化成员变量的一种方式
    {
        cout << "School" << endl;     //在调用School的构造函数之前会先调用Student的构造函数
    }
};

int main()
{
    School s(10);
    system("pause");
    return 0;
}           
c++的构造函数和析构函数

冒号语法来初始化成员变量的方式只要是为了解决:类成员调用有参构造函数来初始化自己,成员变量为常量时,只有通过这种方式才可以对其进行初始化,因为c++定义,常量是不可以被赋值的。另外一个就是引用的变量的初始化,因为引用和常量是有共同的性质的。

class School
{
private:
    const int age;   //声明了一个常量
    int & i;         //声明了一个引用
public:
    School(int & a) :age(10), i(a)   //初始化引用和常量
    {

    }
};           
#include<iostream>
using namespace std;


class A
{
public :
    A(int a)
    {
        cout << "调用: " ;
        cout << "a=" << a << endl;
    }
};
void fun(int a)
{
    static A example(a);             //声明静态对象
    A example1(a+1);                  //声明非静态对象
    cout << "fun a=" << a<<endl;
}
int main()
{
    fun(10);
    fun(20);
    system("pause");
    return 0;
}