在原来的程序中,首先在类里面包含了成员属性并且包含了用来初始化成员属性的一些设置函数,然后在main函数中调用这些设置函数来对这些属性赋值。而且每设置一个属性就要调用一个函数,这样看起来很麻烦效率很低。
因此引入了构造函数。
所谓构造函数就是和类的名字相同的函数,它可以带有参数,也可以带参数,也可以带不同的参数(利用了函数的重载)。
在类中写好了构造函数之后,在使用类定义对象的同时把要赋值给对象的属性的值作为参数同时传进去,那么程序就会自动去调用相应的构造函数(利用函数的重载来区分具体调用那个构造函数(利用的函数重载的属性,通过函数的参数的类型、个数、参数顺序不一样来区分))。
如果在使用类定义对象的时候没有传进去参数那么就调用没有带参数的那个构造函数。
如果想在定义对象的时候不传进去参数就不应该带括号
Person per; /*调用无参构造函数*/ Person是一个类,定义了一个对象per,在定义这个对象的同时会调用不带参数的那个构造函数。
在C语言和C++语言中,圆括号()就代表函数的调用。
Person per(); /*int fun()*/ 这段代码代表一段函数的声明函数名为per 参数为void无参数 返回值为Person类
#include <iostream>
using namespace std;
class Person
{
private:
char *name;
int age;
char *work;
public:
Person()
{
cout<<"Person()"<<endl;
}
Person(char *name)
{
cout<<"Person(char *name)"<<endl;
this->name = name;
}
Person(char *name, int age, char *work = "none")
{
cout<<"Person(char *name, int age, cahr *work)"<<endl;
this->name = name;
this->age = age;
this->work = work;
}
void printInfo(void)
{
cout<<"name = "<<name<<" age = "<<age<<" work = "<<work<<endl;
}
};
int main(void)
{
Person per; /*调用无参构造函数来实例化对象*/
Person per2("zhangsan"); /*调用只有一个参数的构造函数*/
Person per3("lisi", 20); /*调用两个参数的构造函数*/
Person per4("wangwu", 30, "teacher");
/*也可以用new来创建动态对象*/
Person *per5 = new Person();
Person *per6 = new Person[2];
Person *per7 = new Person("wangwu", 20, "student");
per2.printInfo();
per3.printInfo();
per4.printInfo();
per7->printInfo();
/*最后要用delete来释放动态创建的对象*/
delete per5;
delete []per6;
}
利用动态内存分配来保存属性的值
class Person
{
private:
char *name;
int age;
char *work;
public:
Person()
{
cout<<"Person()"<<endl;
}
Person(char *name)
{
cout<<"Person(char *name)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
Person(char *name, int age, char *work = "none")
{
cout<<"Person(char *name, int age, cahr *work)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->age = age;
this->work= new char[strlen(work) + 1];
strcpy(this->work, work);
}
void printInfo(void)
{
cout<<"name = "<<name<<" age = "<<age<<" work = "<<work<<endl;
}
};
分配了堆内存之后在如果没有手动释放,在程序运行结束后操作系统会自动回收堆内存。
但是如果我们分配的堆内存在一个子函数中,当调用这个子函数分配了堆内存,子函数执行完之后并不会释放之前申请的堆内存,这样随着程序的运行时间越长内存的消耗就越大,就会造成内存泄漏。
我们自己可以在类里面定义一个函数用来释放申请过的堆内存,当函数结束之前调用这个释放堆内存的函数。但是这样每个对象都要调用一次这个函数。显得太麻烦了。所以引入了析构函数。在实例化对象销毁的瞬间系统会调用析构函数。可以有很多个构造函数,但是只有一个析构函数并且是不带参数的
如果用new创建的空间应该要用delete来释放才能调用这个对象的析构函数。否则当程序结束释放这个new出来的堆空间不会调用析构函数。
默认拷贝构造函数
Person(Person &per);
定义了一个有参数的构造函数一定要提供一个无参数的构造函数。
person.cpp
#include <iostream>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {cout <<"Pserson()"<<endl;}
Person(char *name)
{
cout <<"Pserson(char *)"<<endl;
this->name = name;
}
Person(char *name, int age)
{
cout <<"Pserson(char*, int)"<<endl;
this->name = name;
this->age = age;
}
void setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int main(int argc, char **argv)
{
Person per("zhangsan", 16);
Person per2;
per.printInfo();
return 0;
}
程序分析:在main函数中使用类Person实例化对象per并且传入两个参数,所以会调用对象per的带有两个参数的构造函数。
在定义对象per2的时候没有传入参数所以会调用无参的构造函数
打印信息:
Pserson(char*, int)
Pserson()
name = zhangsan, age = 16, work = ��c� /*work没初始化所以是乱码*/
person2.cpp
#include <iostream>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {cout <<"Pserson()"<<endl;}
Person(char *name)
{
cout <<"Pserson(char *)"<<endl;
this->name = name;
}
Person(char *name, int age, char *work = "none")
{
cout <<"Pserson(char*, int)"<<endl;
this->name = name;
this->age = age;
this->work = work;
}
void setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int main(int argc, char **argv)
{
Person per("zhangsan", 16);
Person per2; /* 调用无参构造函数 */
Person per3(); /* int fun(); */
per.printInfo();
return 0;
}
程序分析:实例化per对象会调用三个参数的构造函数,如果传入两个参数则第三个参数值为none
实例化per2会调用无参构造函数
第三条语句是一个函数的声明。声明一个函数名为per3返回值为Person不带参数的函数。
打印信息:
Pserson(char*, int)
Pserson()
name = zhangsan, age = 16, work = none
person3.cpp
#include <iostream>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {cout <<"Pserson()"<<endl;}
Person(char *name)
{
cout <<"Pserson(char *)"<<endl;
this->name = name;
}
Person(char *name, int age, char *work = "none")
{
cout <<"Pserson(char*, int)"<<endl;
this->name = name;
this->age = age;
this->work = work;
}
void setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int main(int argc, char **argv)
{
Person per("zhangsan", 16);
Person per2; /* 调用无参构造函数 */
Person per3(); /* int fun(); */
Person *per4 = new Person;
Person *per5 = new Person();
Person *per6 = new Person[2];
Person *per7 = new Person("lisi", 18, "student");
Person *per8 = new Person("wangwu", 18);
per.printInfo();
per7->printInfo();
per8->printInfo();
delete per4;
delete per5;
delete []per6;
delete per7;
delete per8;
return 0;
}
程序分析:相比如之前的程序,先在利用堆内存来个对象分配空间,在使用完后要手动delete堆内存空间。并且因为定义的时候是使用指针来指向堆内存空间的所以在引用对象中的属性或者函数的时候要用符号“->”。
打印结果:
Pserson(char*, int)
Pserson()
Pserson()
Pserson()
Pserson()
Pserson()
Pserson(char*, int)
Pserson(char*, int)
name = zhangsan, age = 16, work = none
name = lisi, age = 18, work = student
name = wangwu, age = 18, work = none
person4.cpp
#include <iostream>
#include <string.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {cout <<"Pserson()"<<endl;}
Person(char *name)
{
cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
Person(char *name, int age, char *work = "none")
{
cout <<"Pserson(char*, int)"<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, work);
}
void setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int main(int argc, char **argv)
{
Person per("zhangsan", 16);
Person per2; /* 调用无参构造函数 */
Person per3(); /* int fun(); */
Person *per4 = new Person;
Person *per5 = new Person();
Person *per6 = new Person[2];
Person *per7 = new Person("lisi", 18, "student");
Person *per8 = new Person("wangwu", 18);
per.printInfo();
per7->printInfo();
per8->printInfo();
delete per4;
delete per5;
delete []per6;
delete per7;
delete per8;
return 0;
}
程序分析:对象的属性使用堆内存来保存属性的值。申请空间的大小的时候要注意分配多一个字节的空间给字符结束符'\0'。
person5.cpp
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {//cout <<"Pserson()"<<endl;
name = NULL;
work = NULL;
}
Person(char *name)
{
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = NULL;
}
Person(char *name, int age, char *work = "none")
{
//cout <<"Pserson(char*, int)"<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, work);
}
void setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
//cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
void test_fun()
{
Person per("zhangsan", 16);
Person per2; /* 调用无参构造函数 */
Person per3(); /* int fun(); */
Person *per4 = new Person;
Person *per5 = new Person();
Person *per6 = new Person[2];
Person *per7 = new Person("lisi", 18, "student");
Person *per8 = new Person("wangwu", 18);
per.printInfo();
per7->printInfo();
per8->printInfo();
delete per4;
delete per5;
delete []per6;
delete per7;
delete per8;
}
int main(int argc, char **argv)
{
for (int i = 0; i < 1000000; i++)
test_fun();
cout << "run test_fun end"<<endl;
sleep(10);
return 0;
}
程序分析:因为在对象销毁之前没有释放对象内部属性所申请的堆内存,所以在程序运行期间会造成内存泄漏。
person6.cpp
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {//cout <<"Pserson()"<<endl;
name = NULL;
work = NULL;
}
Person(char *name)
{
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = NULL;
}
Person(char *name, int age, char *work = "none")
{
//cout <<"Pserson(char*, int)"<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, work);
}
~Person()
{
if (this->name)
delete this->name;
if (this->work)
delete this->work;
}
void setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
//cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
void test_fun()
{
Person per("zhangsan", 16);
Person per2; /* 调用无参构造函数 */
Person per3(); /* int fun(); */
Person *per4 = new Person;
Person *per5 = new Person();
Person *per6 = new Person[2];
Person *per7 = new Person("lisi", 18, "student");
Person *per8 = new Person("wangwu", 18);
per.printInfo();
per7->printInfo();
per8->printInfo();
delete per4;
delete per5;
delete []per6;
delete per7;
delete per8;
}
int main(int argc, char **argv)
{
for (int i = 0; i < 1000000; i++)
test_fun();
cout << "run test_fun end"<<endl;
sleep(10);
return 0;
}
程序分析:程序中添加了析构函数,析构函数就是~Person()这个函数,波浪线+类的名字并且不带参数,当对象在销毁之前会调用这个析构函数,析构函数只有一个,构造函数可以有多个。所以这个程序不会有内存泄漏。
person7.cpp
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {//cout <<"Pserson()"<<endl;
name = NULL;
work = NULL;
}
Person(char *name)
{
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = NULL;
}
Person(char *name, int age, char *work = "none")
{
//cout <<"Pserson(char*, int)"<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, work);
}
~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 setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
//cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
void test_fun()
{
Person per("zhangsan", 16);
Person *per7 = new Person("lisi", 18, "student");
//delete per7;
}
int main(int argc, char **argv)
{
test_fun();
cout << "run test_fun end"<<endl;
sleep(10);
return 0;
}
程序分析:当对象被销毁之前会调用析构函数。但是使用了new为对象分配堆空间而没有delete等程序退出的时候是不会调用析构函数的。
只有调用了delete之后才会调用析构函数。
所以打印结果:/*只调用了per这个对象的析构函数*/
~Person()
name = zhangsan
work = none
run test_fun end
person8.cpp
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {//cout <<"Pserson()"<<endl;
name = NULL;
work = NULL;
}
Person(char *name)
{
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = NULL;
}
Person(char *name, int age, char *work = "none")
{
//cout <<"Pserson(char*, int)"<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, work);
}
~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 setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int main(int argc, char **argv)
{
Person per("zhangsan", 18);
Person per2(per);
per2.printInfo();
return 0;
}
程序分析:默认拷贝构造函数,将per2的对象的属性的指针也指向了per对象的属性的指针所指向的内存空间,所以当这两个对象销毁的时候会调用两次析构函数,然后对同一个内存区域释放两次。
打印结果:
name = zhangsan, age = 18, work = none
~Person()
name = zhangsan
work = none
~Person()
name = /*因为上面已经释放了这两个指针指向的内存区域,所以没东西打印出来*/
work =
person9.cpp
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {//cout <<"Pserson()"<<endl;
name = NULL;
work = NULL;
}
Person(char *name)
{
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = NULL;
}
Person(char *name, int age, char *work = "none")
{
//cout <<"Pserson(char*, int)"<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, 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);
}
~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 setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
int main(int argc, char **argv)
{
Person per("zhangsan", 18);
Person per2(per);
per2.printInfo();
return 0;
}
程序分析:自己写了一个拷贝构造函数,利用了引用把per对象的属性的值都传给per2对象的属性。两个对象的属性的指针都分别指向不同的内存空间。
打印结果:
Pserson(Person &)
name = zhangsan, age = 18, work = none
~Person()
name = zhangsan
work = none
~Person()
name = zhangsan
work = none
person10.cpp
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
char *name;
int age;
char *work;
public:
Person() {
cout <<"Pserson()"<<endl;
name = NULL;
work = NULL;
}
Person(char *name)
{
//cout <<"Pserson(char *)"<<endl;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = NULL;
}
Person(char *name, int age, char *work = "none")
{
cout <<"Pserson(char*, int), name = "<<name<<", age= "<<age<<endl;
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->work = new char[strlen(work) + 1];
strcpy(this->work, 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);
}
~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 setName(char *n)
{
name = n;
}
int setAge(int a)
{
if (a < 0 || a > 150)
{
age = 0;
return -1;
}
age = a;
return 0;
}
void printInfo(void)
{
//printf("name = %s, age = %d, work = %s\n", name, age, work);
cout<<"name = "<<name<<", age = "<<age<<", work = "<<work<<endl;
}
};
class Student {
private:
Person father;
Person mother;
int student_id;
public:
Student()
{
cout<<"Student()"<<endl;
}
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;
}
};
int main(int argc, char **argv)
{
Student s(100, "bill", "lily");
return 0;
}
程序分析:一个类(Person)中对象作为另外一个类(Student)的成员属性,当实例化这个类(Student)的时候会首先调用被包含类(Person)的构造函数(默认是调用无参构造函数)。并且可以在这个类(Student)的构造函数中给另外一个类(Person)的对象传入参数。。调用析构函数的顺序则相反。
打印结果:
Pserson(char*, int), name = bill, age= 40
Pserson(char*, int), name = lily, age= 39
Student(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