C++提供了构造函数来处理对象的初始化,构造函数是一种特殊的成员函数,与其他成员函数不同,它不需要用户来调用,而是在建立对象时自动执行。构造函数名称必须与类同名,而不能由用户任意命名,以便编译系统能识别它把它作为构造函数处理。它不具体任何类型,也不返回任何值。构造函数的功能由用户定义,可根据初始化的要求设计函数体和函数参数。而且如果用户未定义构造函数,C++系统会自动生成一个构造函数,只是这个构造函数体是空的。
一、默认构造函数
用户未定义构造函数系统会自动生成一个构造函数,这个构造函数的函数体是空的,也没有参数,不执行初始化操作。代码如下:
#include <iostream>
#include <string>
using namespace std;class Student{private:int age;string name;string sex;public:void display(){cout <<"name:" <<name <<", age:" <<age <<", sex:" <<sex <<endl;}
};int main(){Student s;s.display();return 0;
}
执行效果如下:
二、自定义构造函数
构造函数没有返回值,也不需要在定义构造函数时声明类型。构造函数是在建立对象时由系统自动执行的,而且只执行一次。这里创建一个无参构造函数,代码如下:
#include <iostream>
#include <string>
using namespace std;class Student{private:int age;string name;string sex;public: Student(){age = 0;name = "test";sex = "male";}void display(){cout <<"name:" <<name <<", age:" <<age <<", sex:" <<sex <<endl;}
};int main(){Student s;s.display();return 0;
}
此时参数已在构造函数中初始化,执行效果如下:
三、构造函数的重载
在一个类中可以定义多个构造函数,以便对类对象提供不同的初始化的方法供用户选择。这些构造函数名称相同,而参数的个数或参数类型不同,这称为构造函数的重载。代码如下:
#include <iostream>
#include <string>
using namespace std;class Student{private:int age;string name;string sex;public: // 不带参数构造函数Student(){age = 0;name = "test";sex = "male";}// 带参的构造函数Student(int a, string n, string s){age = a;name = n;sex = s;}void display(){cout <<"name:" <<name <<", age:" <<age <<", sex:" <<sex <<endl;}
};int main(){Student s, s2(10, "jack", "male");s.display();s2.display();return 0;
}
运行结果如下:
四、参数初始化表
C++提供了一种特殊的初始化数据成员方法(参数初始化表来实现对数据成员的初始化),这种方法不在函数体内实现数据的初始化,而在函数首部实现。代码如下:
#include <iostream>
#include <string>
using namespace std;class Student{private:int age;string name;string sex;public: // 不带参数构造函数Student(){age = 0;name = "test";sex = "male";}// 带参的构造函数,并且使用参数初始化表对数据成员的初始化Student(int a, string n, string s) : age(a), name(n), sex(s){}void display(){cout <<"name:" <<name <<", age:" <<age <<", sex:" <<sex <<endl;}
};int main(){Student s, s2(10, "jack", "male");s.display();s2.display();return 0;
}
这种写法方便、简练,尤其当需要初始化的数据成员较多时更显其优势。运行结果如下:
五、类体外定义构造函数
除在类内定义构造函数,也可以在类外定义构造函数,要类名加上两个冒号“::”域限定符来实现。代码如下:
#include <iostream>
#include <string>
using namespace std;class Student{private:int age;string name;string sex;public: // 声明不带参数构造函数Student();// 带参的构造函数,并且使用参数初始化表对数据成员的初始化Student(int, string, string);void display(){cout <<"name:" <<name <<", age:" <<age <<", sex:" <<sex <<endl;}
};int main(){Student s, s2(10, "jack", "male");s.display();s2.display();return 0;
}// 类体外定义无参构造函数
Student::Student(){age = 0;name = "test";sex = "male";
}
// 在类体外定义带参构造函数
Student::Student(int a, string n, string s) : age(a), name(n), sex(s){}
运行结果如下:
六、构造函数添加默认参数
在成员函数中可以使用有默认的参数,构造函数也可以使用包含默认参数的构造函数。代码如下:
Student::Student(int a = 15, string n = "Tom", string s = "female") : age(a), name(n), sex(s){}
如果构造函数全部参数都指定了默认值,则在定义对象时可以给一个或几个实参,也可以不给出实参;但应该执行哪一个构造函数,会出现歧义,系统无法识别编译时会报错([Error] no matching function for call to 'Student::Student()')。这块操作对程序员来说,开发难度会加大,所以不建议使用。
七、析构函数
析构函数是C++中的一个特殊的成员函数,它的作用与构造函数相反,它的名字是类名前面加一个“~”符号。在以C++中“~”是位取反运算符,所它是与构造函数作用相反的函数。当对象的生命期结束时,会自动执行析构函数。在以下几种情况会执行析构函数:
- 在一个函数中定义一个对象,当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
- static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。
- 定义了一个全局对象,则在程序的流程离开其作用域时,调用该全局对象的析构函数。
- 如果用new运算符动态地建立一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。
以上几种方法演示代码如下:
#include <iostream>
#include <string>
using namespace std;class Student{private:int age;string name;string sex;public: // 声明不带参数构造函数Student();// 带参的构造函数,并且使用参数初始化表对数据成员的初始化Student(int, string, string);// 定义析构函数~Student(){cout <<"Destructor called:" <<name <<endl;}void display(){cout <<"name:" <<name <<", age:" <<age <<", sex:" <<sex <<endl;}
};// 对象在函数内执行
void show_student(){Student t1(15, "Lily", "femail");t1.display();
}int main(){// 在函数中定义一个对象执行后,执行析构函数show_student();// 当对象定义全局(如main函数结束),执行析构函数Student s;s.display();// 当static局部对象在main函数结束后,执行析构函数static Student s2(10, "jack", "male");s2.display();// 当用new运算符动态地建立一个对象Student *p = new Student(12, "John", "male");p->display();return 0;
}// 类体外定义无参构造函数
Student::Student(){age = 0;name = "test";sex = "male";
}
// 在类体外定义带参构造函数
Student::Student(int a = 15, string n = "Tom", string s = "female") : age(a), name(n), sex(s){}
执行后支行结果:
可以看出,首先show_student()函数执行完后,其内部t1对象已结束并执行析构函数;然后定义全局对象和static局部对象,在main函数执行结束后,也相继执行析构函数;但是始终未见用new运算符动态建立的对象执行析构函数,这是因为没用delete运算符释放对象,现在咱们加再运行看下结果,代码如下:
Student *p = new Student(12, "John", "male");
p->display();
delete p;
运行结果如下:
此时用new建立的对象也执行了析构函数,从这也可以看出,John在test、jack之前执行析构函数,恰好说明了定义在全局对象和static局部对象是在main函数执行结束后,执行的析构函数。
c++构造函数与其他开发语言相比,不仅有很多相似之处,也有独特之处。相似之处有其命名中与类同名且无返回类型,自动调用与初始化,可重载性等外,还可以在类体外定义构造函数,参数初始化表,析构函数等。