构造函数
所谓构造函数就是跟类名相同的函数,可以带有参数也可以不带参数。可以通过参数的不用,来区分调用哪个构造函数
类的构造方法的概念和作用:
- 构造方法负责对象初始化工作,为对象属性赋合适的初始值
- 创建对象时,其类的构造方法确保在用户操作对象之前,系统保证初始化的进行
构造方法的语法规则:
- 构造函数名和类名一致
- 没有返回类型
- 方式实现主要为字段赋值
demo
#include <iostream>
using namespace std;
class Person{
char *name;
int age;
char *work;
public:
Person(){
cout<<"Person()"<<endl;
}
Person(char* name, int age, char* work = "none"){
cout<<"Person(char*, int)"<<endl;
this->name = name;
this->age = age;
this->work = work;
}
void printfInfo(){
cout<<"work = "<<work<<" name = "<<name<<" age = "<<age<<endl;
}
};
int main(int argv, char** argc)
{
Person per("zhangsan",18);
Person per1; //这是调用没有参数的构造体
Person per2(); //这是定义一个函数,Person型的函数,相当于int fun();
per.printfInfo();
}
程序结果
Person(char*, int)
Person()
work = none name = zhangsan age = 18
在C++中,可以通过指针实例化类,程序结束后,指针会被自动释放。如果你想要手动释放的话,可以使用delete命令。
demo
#include <iostream>
using namespace std;
class Person{
char *name;
int age;
char *work;
public:
Person(){
cout<<"Person()"<<endl;
}
Person(char* name, int age, char* work = NULL){
cout<<"Person(char*, int)"<<endl;
this->name = name;
this->age = age;
}
void printfInfo(){
cout<<"work = "<<work<<" name = "<<name<<" age = "<<age<<endl;
}
};
int main(int argv, char** argc)
{
Person per("zhangsan",18);
Person per1; //这是定义没有参数的构造体
Person per2(); //这是定义一个函数,Person型的函数
/*通过指针实例化类*/
/*以下两种实例方法是等同的,都是调用没有参数的构造体*/
Person *per3 = new Person;
Person *per4 = new Person();
Person *per5 = new Person[2];//也可以定义成数组
Person *per6 = new Person("lisi", 16, "student");//也可以添加参数
/*程序结束后,指针会被自动释放,如果像手动释放的话用以下方法*/
delete per3;
delete []per5;//释放数组的方法
per6->printfInfo();
per.printfInfo();
}
程序结果:
Person(char*, int)
Person()
Person()
Person()
Person()
Person()
Person(char*, int)
在main函数中用new函数开辟的空间,会自动释放内存。但是在构造函数中利用new函数开辟了一个堆空间和子程序中的开辟空间需要我们自己释放。如果不释放则会占用我们的内存。
demo
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person{
char *name;
int age;
char *work;
public:
Person(){
cout<<"Person()"<<endl;
}
Person(char* name){
//cout<<"Person(char*, int)"<<endl;
/*在构造函数中利用new函数开辟了一个堆空间需要我们自己释放*/
this->name = new char[strlen(name)+1];
strcpy(this->name,name);//将参数名字放入开辟的空间之内
this->work = NULL;//没有work这个参数,要赋值一个null给他,否则析构函数中会出现段错误
}
Person(char* name, int age, char* work = "none"){
// cout<<"Person(char*, int)"<<endl;
this->name = new char[strlen(name)+1];
strcpy(this->name,name);
this->work = new char[strlen(work)+1];
strcpy(this->work,work);
this->age = age;
}
void printfInfo(){
cout<<" name = "<<name<<" age = "<<age<<endl;
}
};
int testFun()
{
Person per("zhangsan",18);
Person *per6 = new Person("lisi", 16, "student");//子程序中的空间需要我们自己释放
delete per6;
}
int main()
{
int i;
for(i = 0; i < 100000; i++)
testFun();//在执行该函数的时候一直在消耗空间内存,并没有去释放构造函数中new出来的空间
sleep(10);
return 0;
}
可以通过free命令查看当前空闲内存空间的大小。
free -m #-m是指输出的信息以兆为单位;
从以下可以看出,在运行过程中会占用内存空间。
book@100ask:~$ free -m//运行前
total used free shared buff/cache available
Mem: 1959 1129 350 15 479 642
Swap: 8581 171 8410
book@100ask:~$ free -m//运行时
total used free shared buff/cache available
Mem: 1959 1144 335 15 479 627
Swap: 8581 171 8410
book@100ask:~$ free -m//运行后
total used free shared buff/cache available
Mem: 1959 1129 350 15 479 642
Swap: 8581 171 8410
book@100ask:~$
那怎么去释放构造函数中的内存空间呢?有两种方法:
第一种方法就是在引入一个人free函数去释放空间,在free函数中去delete掉开辟的空间,然后再在子程序中调用free函数就可以了。但是这种方法非常麻烦,看起来也不高级。
第二种方法就是引入析构函数,那么什么是析构函数呢?
析构函数
析构函数:是一个特殊的成员函数,它的作用与构造函数相反,它的名字是类名的前面加一个“~”符号。
析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。程序设计者事先设计好析构函数,以完成所需的功能,只要对象的生命期结束,程序就自动执行析构函数来完成这些工作。
注意:析构函数不返回任何值,没有函数类型,也没有函数参数。因此它不能被重载。一个类可以有多个构造函数,但只能有一个析构函数。
demo
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person{
char *name;
int age;
char *work;
public:
Person(){
cout<<"Person()"<<endl;
}
Person(char* name){
cout<<"Person(char*)"<<endl;
/*开辟一个空间储存名字,但是子程序中的空间需要我们自己释放*/
this->name = new char[strlen(name)+1];
strcpy(this->name,name);//将参数名字放入开辟的空间之内
this->work = NULL;//没有work这个参数,要赋值一个null给他,否则析构函数中会出现段错误
}
Person(char* name, int age, char* work = "none"){
cout<<"Person(char*, int)"<<endl;
this->name = new char[strlen(name)+1];
strcpy(this->name,name);
this->work = new char[strlen(work)+1];
strcpy(this->work,work);
this->age = age;
}
~Person(){//析构函数,有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
if(this->name){
cout<<"name ="<<name<<endl;
delete this->name;
}
if(this->work){
cout<<"work ="<<work<<endl;
delete this->work;
}
}
void printfInfo(){
cout<<" name = "<<name<<" age = "<<age<<endl;
}
};
int testFun()
{
Person per("zhangsan",18);
Person *per6 = new Person("lisi", 16, "student");//也可以添加参数
delete per6;//手动释放空间
}
int main()
{
testFun();
sleep(10);
return 0;
}
程序结果:从结果可以看出,当testFun()函数执行完之后,系统会自动调用析构函数,释放内存空间。而在testFun()利用new实例化的对象则需要手动调用delete释放空间,否则testFun()函数执行完成之后也不会调用析构函数释放该空间,只能等到程序结束之后系统来释放。
Person(char*, int)
Person(char*, int)
~Person()
name =lisi
work =student
~Person()
name =zhangsan
work =none
内存空闲空间可以看出,调用析构函数之后,就没有占用内存空间了。
book@100ask:~$ free -m #运行前
total used free shared buff/cache available
Mem: 1959 1126 370 15 461 645
Swap: 8581 171 8410
book@100ask:~$ free -m #运行中
total used free shared buff/cache available
Mem: 1959 1126 370 15 462 645
Swap: 8581 171 8410
book@100ask:~$ free -m #运行后
total used free shared buff/cache available
Mem: 1959 1126 371 15 462 646
Swap: 8581 171 8410
book@100ask:~$
具体地说如果出现以下几种情况,程序就会执行析构函数:
①如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
②static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。
③如果定义了一个全局对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。
④如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。
拷贝函数
Person per("zhangsan", 18, "teacher");
/*打印的与per内容一致,会调用默认的拷贝函数*/
Person per1(per);//值拷贝,有一个隐患,会对同一个空间释放两次,不建议默认调用拷贝构造函数
//可以利用引用拷贝,解决该隐患
可以使用拷贝函数解决一个空间被释放两次的隐患。
拷贝函数原型:
Person(Person &per){//拷贝函数,这样就不.会对同一个地址析构两次
cout<<"Person(Person &)"<<endl;
this->name = new char[strlen(per.name)+1];
strcpy(this->name,per.name);
this->work = new char[strlen(per.work)+1];
strcpy(this->work,per.work);
this->age = per.age;
}
构造函数被调用的顺序
demo
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person{
char *name;
int age;
char *work;
public:
Person(){
name = NULL;
work = NULL;
}
Person(char* name){
/*开辟一个空间储存名字,但是子程序中的空间需要我们自己释放*/
this->name = new char[strlen(name)+1];
strcpy(this->name,name);//将参数名字放入开辟的空间之内
this->work = NULL;//没有work这个参数,要赋值一个null给他,否则析构函数中会出现段错误
}
Person(char* name, int age, char* work = "none"){
cout<<"Person(char*, int)"<<endl;
this->name = new char[strlen(name)+1];
strcpy(this->name,name);
this->work = new char[strlen(work)+1];
strcpy(this->work,work);
this->age = age;
printfInfo();
}
Person(Person &per){//拷贝函数
cout<<"Person(Person &)"<<endl;
this->name = new char[strlen(per.name)+1];
strcpy(this->name,per.name);
this->work = new char[strlen(per.work)+1];
strcpy(this->work,per.work);
this->age = per.age;
}
~Person(){//析构函数,有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
cout<<"~Person()"<<endl;
if(this->name){
cout<<"name ="<<name<<endl;
delete this->name;
}
if(this->work){
cout<<"work ="<<work<<endl;
delete this->work;
}
}
void printfInfo(){
cout<<" name = "<<name<<" age = "<<age<<"work = "<<work<<endl;
}
};
Person per_g("per_g",10);
void per_func()
{
Person per_func("per_func",11);
static Person per_func_s("per_func_s",12);
}
int main(int argc, char** argv)
{
Person per_main("per_main", 13);
static Person per_main_s("per_main_s", 14);
for(int i=0; i<2; i++){
per_func();
Person per_for("per_for",i);
}
return 0;
}
程序结果:
析构函数不会销毁static类型的实例化对象开辟的空间。所以static类型的实例化对象只会被调用一次。
构造顺序:按运行中定义对象的顺序调用构造函数 ,全局对象在main函数执行前被调用。
Person(char*, int)
name = per_g age = 10work = none
Person(char*, int)
name = per_main age = 13work = none
Person(char*, int)
name = per_main_s age = 14work = none
Person(char*, int)
name = per_func age = 11work = none
Person(char*, int)
name = per_func_s age = 12work = none
~Person()
name =per_func
work =none
Person(char*, int)
name = per_for age = 0work = none
~Person()
name =per_for
work =none
Person(char*, int)
name = per_func age = 11work = none
~Person()
name =per_func
work =none
Person(char*, int)
name = per_for age = 1work = none
~Person()
name =per_for
work =none
~Person()
name =per_main
work =none
~Person()
name =per_func_s
work =none
~Person()
name =per_main_s
work =none
~Person()
name =per_g
work =none
在一个类中调用另一个类
要调用对象成员的其他构造函数,可以这样写:
class Student{
private:
person id;
public:
Student(int sid):id(sid){}
}
构造函数的“{}”前加上“:”,加上成员的初始化代码。
对象成员:定义时的构造顺序与上述初始化顺序无关。
demo
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person{
char *name;
int age;
char *work;
public:
Person(){
name = NULL;
work = NULL;
}
Person(char* name){
/*开辟一个空间储存名字,但是子程序中的空间需要我们自己释放*/
this->name = new char[strlen(name)+1];
strcpy(this->name,name);//将参数名字放入开辟的空间之内
this->work = NULL;//没有work这个参数,要赋值一个null给他,否则析构函数中会出现段错误
}
Person(char* name, int age, char* work = "none"){
cout<<"Person(char*, int)"<<endl;
this->name = new char[strlen(name)+1];
strcpy(this->name,name);
this->work = new char[strlen(work)+1];
strcpy(this->work,work);
this->age = age;
printfInfo();
}
Person(Person &per){//拷贝函数
cout<<"Person(Person &)"<<endl;
this->name = new char[strlen(per.name)+1];
strcpy(this->name,per.name);
this->work = new char[strlen(per.work)+1];
strcpy(this->work,per.work);
this->age = per.age;
}
~Person(){//析构函数,有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
cout<<"~Person()"<<endl;
if(this->name){
cout<<"name ="<<name<<endl;
delete this->name;
}
if(this->work){
cout<<"work ="<<work<<endl;
delete this->work;
}
}
void printfInfo(){
cout<<" name = "<<name<<" age = "<<age<<"work = "<<work<<endl;
}
};
class Student{
private:
Person father;
Person mother;
int student_id;
public:
Student(){//当你提供了一个有参数的构造函数时,默认的无参构造函数就会消失,必须设置一个无参数的构造函数,这样才不会报错
}
Student(int id, char* father, char* mother, int father_age = 40, int mother_age = 39) : father(father,father_age), mother(mother,mother_age)
{
cout<<"Studen(int id, char* father, char* mother, int father_age = 40, int mother_age = 39)"<<endl;
}
~Student(){//析构函数的执行顺序与构造函数执行顺序相反
cout<<"~Student()"<<endl;
}
};
int main(int argc, char** argv)
{
Student s(100, "bill", "lily");//调用构造函数
}
程序结果:由程序结果可以得出,调用构造函数的顺序与实例化对象的顺序有关。析构函数的执行顺序与构造函数执行顺序相反。
Person(char*, int)
name = bill age = 40work = none
Person(char*, int)
name = lily age = 39work = none
Studen(int id, char* father, char* mother, int father_age = 40, int mother_age = 39)
~Student()
~Person()
name =lily
work =none
~Person()
name =bill
work =none