课程视频录制于:2016年04月19日
一、构造函数
1.为什么引入构造函数
2.所谓构造函数就是和类名相同的函数,可以带参数也可不带参数,也可以带有不同的参数。
3.在构造类对象的时候,根据传进来的参数来判断是调用哪一个构造函数。
4.如果一个没有参数的构造函数,那么在构造类对象的时候不要写圆括号(因为学了圆括号不是构造对象,而是声明)。
5.在构造函数的参数里有这样一个参数 char* work="none" ,在不给这个参数传入时,默认就是none。
6.实例化对象的方法:
Person per("zhangsan", 16);
Person per2; /* 调用无参构造函数 */
Person per3(); /* int fun(); 这只是一个声明不是一个调用*/
/*********用指针来实例化对象*****/
Person *per4 = new Person;
Person *per5 = new Person(); //per4和per5完全相同
Person *per6 = new Person[2]; //实例化了两个对象
Person *per7 = new Person("lisi", 18, "student");
Person *per8 = new Person("wangwu", 18);
// 因为最后一个参数是work,在定义时赋值了“none”,所以不赋值时默认时“none”
/*********不同的对象调用成员函数*****/
per.printInfo();
per7->printInfo();
per8->printInfo();
7.动态创建的对象要如果释放?程序执行完毕之后会自动释放,用delete命令可以手工释放
delete per4;
delete per5;
delete []per6; //因为person6是一个数组
delete per7;
delete per8;
8.在构造函数里可以开辟一块空间来储存变量的值:
Person(char *name){
cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);}
9.在主函数(main函数)没有执行完时,用new分配的内存不会被释放掉,在整个主函数全部执行完毕才会将new分配的内存全部释放。有两种方法来释放:1.写一个free函数 调用delete来一个个把new出来内存都释放掉 2.使用析构函数
10.析构函数的使用:
在函数名之前加上一个波浪号~,表示这是一个析构函数。析构函数可以防止内存泄漏
~Person(){
if (this->name)
delete this->name;
if (this->work)
delete this->work;}
11.很多构造函数只有一个析构函数,并且析构函数没有参数
12.析构函数在实例化对象在销毁瞬间被调用
13.用new创建的实例化对象,如果不手动释放,在主函数执行完毕之后也会被释放,但是并不是使用析构函数释放的。如果手动释放new创建的实例化对象时就会调用析构函数。(如果有析构函数的话)
14.不是使用new创建的实例化对象,手动或者系统自动释放都是调用的析构函数。(如果有析构函数的话)
15.建议如果用new实例化对象的话,用delete手动释放
16.如果没有在类里定义默认构造函数和析构函数的话,系统会默认给我们加上构造函数和析构函数,并且这两个函数都为空。
17.C++里还会提供 默认拷贝构造函数
这段程序里,per2和per的内容是相同的,即使我们没有定义参数是类的构造函数,也就是系统自动给我们提供了默认的拷贝构造函数,这段程序的含义是把per内的内容全部拷贝给per2
Person per("zhangsan", 18);
Person per2(per);
per2.printInfo()
18.默认拷贝构造函数是 值拷贝,会存在的隐患是:如果将per的内存释放后,在释放per2时,会释放per的内存,也就是同一块内存可能会被释放两次。两个对象的*name和*work都指向同一块内存。为了避免这种问题我们需要自己写自己的构造函数:
Person(Person &per)
{
cout <<"Pserson(Person &)"<<endl;
this->age = per.age;
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);
}
用new来重新分配空间,在进行对象拷贝时,新的对象指针变量会指向新的内存。
19.构造函数的构造顺序:
首先是全局变量,main函数,然后根据执行顺序碰到哪个实例化对象就调用哪一个构造函数。
按运行中定义对象的顺序调用构造函数,静态对象只调用一次构造函数;全局对象在main函数执行前被构造
20.子函数中静态的实例化对象在子函数执行结束后也不会调用析构函数来销毁,当再次调用子函数时,遇到静态的实例化对象就不会再次创建了。
21.如果类里面有个对象的话是如何初始化的?
类里面有对象成员的话,按照写的顺序来调用构造函数。
22.如果定义了有参数的构造函数,必须提供一个无参数的构造函数,如果不提供就会编译出错。因为如果自己定义了有参数的构造函数,那么系统不会在提供默认的无参数的构造函数。
23.这些实例化对象的析构函数被调用的顺序和被构造的顺序相反,先构造的后析构。
24.在类内实例化对象时用参数初始化值
class Student {
private:
Person father;
Person mother;
int student_id;
public:
Student()
{
cout<<"Student()"<<endl;
}
/******在这个构造函数里,有参数,这种形式就是初始化father和mother内的数据成员********/
Student(int id, char *father, char *mother, int father_age = 40, int mother_age = 39) : mother(mother, mother_age), father(father, father_age)
{
cout<<"Student(int id, char *father, char *mother, int father_age = 40, int mother_age = 39)"<<endl;
}
~Student()
{
cout<<"~Student()"<<endl;
}
};
/************实例化对象的顺序和写数据成员的顺序一致*******************/
在Student类里,在实例化father和mother时,用的Student构造函数的参数初始化对象father和mother里的数据成员
/************实例化对象的顺序和写数据成员的顺序一致*******************/