1、构造函数和析构函数
(1)构造函数(constructor):字面意思就是构造对象的函数,当我们定义类的对象时调用,可以帮我们完成对象的初始化;
(2)析构函数(destructor):可以看做是构造函数的逆过程,当销毁类的时候调用,做一些收尾的工作;比如:定义类的对象时构造函数申请了动态内存,那在销毁对象时调研员的析构函数中就要释放申请的动态内存,否则就会造成内存泄漏;
(3)构造/析构函数是C++语言自带的回调函数。程序员可以在定义类时使用默认的构造/析构函数,或者自己定义构造/析构函数,但是构造/析构函数的调用却不是程序员主动去调用,而是C++语言自动帮我们调用。比如:定义类的对象时就会自动调用构造函数,销毁类的时候就会调用析构函数,程序员只需要定义类或者销毁类,构造/析构函数的调用是自动的;
2、构造函数和析构函数的使用
(1)C++在定义类时会提供默认的构造/析构函数,默认的构造/析构函数可以看做是空函数,什么也没做,如果我们没有需求可以在类中不定义构造/析构函数;
(2)如果我们在类中定义了构造/析构函数,则C++提供的默认构造/析构函数就失效了,只会调用自定义的构造/析构函数;
(3)构造和析构函数不需要返回值类型,构造函数可以带参或不带参,析构函数不带参;
(4)构造函数可以重载(overload),析构函数不需要重载;
(5)构造函数的名字就是类的名字,析构函数的名字是"~ + 类名";
3、为什么需要构造函数和析构函数
(1)在C语言中只有struct没有class,所以C语言没有构造/析构函数的概念,C语言程序员定义struct后,需要手动去给struct结构体成员初始化;
(2)C++是面向对象的语言,class的封装性比struct更好。
(3)比如:class中有个成员变量是指针,如果没有构造/析构函数,则需要定义对象后手动去为指针申请动态内存,在销毁对象前,手动去释放动态内存;现在动态内存的申请/释放可以放到构造/析构函数中,使用class的程序员不必再操心动态内存的申请释放;
4、不使用动态内存的构造/析构函数
4.1、示例代码
#include <iostream>
using namespace std;
class Person{
public:
int age;
string getName(void) const;
//构造函数
Person();
Person(string myname);
// 析构函数
~Person();
private:
string name; //人的名字
mutable int getNameCnt; //名字被查询的次数
};
int main(void)
{
//定义对象,此时会调用构造函数
Person *p1 = new Person ("linux");
cout << "p1.name=" << p1->getName() << endl;
// 销毁对象
delete(p1);
return 0;
}
string Person::getName(void) const
{
this->getNameCnt++;
cout << "getNameCnt=" << this->getNameCnt << endl;
return this->name;
}
// 使用参数列表的方式
Person::Person()
{
cout << "Person::Person()" << endl;
this->getNameCnt=0;
};
// 构造函数:使用参数列表的方式
Person::Person(string myname):name(myname)
{
//this->name=myname; //等同于上面参数列表的形式
cout << "Person::Person(string myname):name(myname)" << endl;
this->getNameCnt=0;
};
// 析构函数:在没有使用动态内存的情况下,析构函数一般都是空函数
Person::~Person()
{
cout << "~Person" << endl;
};
4.2、代码分析
root@ubuntu:# ./app
Person::Person(string myname):name(myname)
getNameCnt=1
p1.name=linux
~Person
(1)在定义对象时调用了构造函数,调用的是传string形式的,因为构造函数支持函数重载,并且定义定义对象时传入了string的参数;
(2)在销毁对象时调用了析构函数;
5、使用动态内存的构造/析构函数
5.1、示例代码
#include <iostream>
using namespace std;
class Person{
public:
int age;
string getName(void) const;
//构造函数
Person();
Person(string myname);
// 析构函数
~Person();
private:
string *name; //人的名字
mutable int getNameCnt; //名字被查询的次数
};
int main(void)
{
//定义对象,此时会调用构造函数
Person *p1 = new Person ("linux");
cout << "p1.name=" << p1->getName() << endl;
// 销毁对象
delete(p1);
return 0;
}
string Person::getName(void) const
{
this->getNameCnt++;
cout << "getNameCnt=" << this->getNameCnt << endl;
return *this->name;
}
// 使用参数列表的方式
Person::Person()
{
cout << "Person::Person()" << endl;
this->getNameCnt=0;
};
// 构造函数
Person::Person(string myname)
{
this->name = new string(); //申请动态内存
*this->name=myname;
cout << "Person::Person(string myname):name(myname)" << endl;
this->getNameCnt=0;
};
// 析构函数
Person::~Person()
{
cout << "[~Person] delete this->name" << endl;
delete (this->name); //释放在构造函数中申请的动态内存
};
5.2、代码分析
root@ubuntu:# ./app
Person::Person(string myname):name(myname)
getNameCnt=1
p1.name=linux
[~Person] delete this->name
(1)上第4节的代码相比,将class中name变量从string类型变成string指针类型;
(2)在构造函数中为string指针申请内存,在析构函数中释放内存;
总结:如果类中使用动态内存,可以在构造函数中申请,在析构函数中释放,使用类的程序员不用再操作内存的申请释放;
6、拷贝构造函数
参考博客:《【C++入门】拷贝构造函数》;