1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数(系统默认生成的拷贝构造函数,只负责进行简单的赋值操作,即浅拷贝),对属性进行值拷贝
构造函数调用规则如下:
-
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
-
如果用户定义拷贝构造函数,c++不会再提供其他构造函数
- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1.构造函数的作用:(初始化时构造函数就开始发挥作用)
“构造函数用来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,默认无参数的构造函数不需要用户来调用它,而是在建立对象时自动执行。构造函数的功能是由用户定义的,用户根据初始化的要求设计函数体和函数参数。”
2.构造函数的注意事项:
①、构造函数的名字必须与类名同名,不能随意命名,这样的话才能让编译器认为该函数是构造函数,而不是类的普通成员函数;
②、构造函数不具有任何类型,不返回任何值,连 void 都不是;
③、默认的构造函数不需要用户调用,它是对象建立的时候自动被系统调用,用来初始化刚刚建立的对象的;
④、如果用户没有定义自己的类的构造函数,那么系统会自动生成一个默认的构造函数,只不过该构造函数的函数体是空的,也就是什么都不做。
总结:构造函数是用来初始化值的,用于类对象的。
代码例子:
(1)调用无参构造函数
1. Class Student{ int age; string name; } int main(){ Student stu[100];//这样会创建一百个对象,默认会调用空的构造函数,即会默认等价于下面这段函数。 }
2. Class Student{ int age; string name; Student(){} } int main(){ Student stu[100];//这样会创建一百个对象,默认会调用空的构造函数,即会默认等价于上面这段函数。
//Student stu[100]==Student stu[100]() 默认调用构造函数
}
(2)调用有参构造函数
#include <iostream> using namespace std; class Student{ public: int age; Student(){std::cout << "101" << std::endl;} Student(int age){ this->age = age; std::cout << "你好" <<age<<"岁了"<< std::endl; } }; int main() { Student stu0(60);//单个调用有参数的构造函数 Student stu1[3]={Student(30),Student(30),Student(30)};//数组对象调用有参数的构造函数 }
运行结果:
2.析构函数(当生命周期快结束时才调用析构函数,可以理解构造函数与析构函数相反,一般用于内存释放,清理空间)
析构函数也是一个在类中跟构造函数类似的特殊功能的成员函数。只不过它的作用是与构造函数相反,是在对象的生命周期结束的时候会被自动调用的。在C++中析构函数的名字跟类名相同,并且前面带上一个取反的符号~,表达的意思也就是跟构造函数的过程相反。
默认情况下,如果类的设计者没有自己定义析构函数,那么编译器会自动为该类生成一个默认的析构函数,只不过函数体是空的,也就是什么都没做。所以,如果需要在对象被删除的时候做一些操作的话,那么就得自己定义析构函数喽。
以下几种情况会自动调用析构函数:
①、如果在一个函数中定义了一个局部变量的对象,那么当这个函数执行结束时也就是该变量对象生命周期结束的时候,所以析构函数会被自动调用;
②、全局变量或者static类型的变量,他们的生命周期一般是在程序退出的时候,这时候该对象的析构函数才会被调用;
③、如果是用new操作符动态的创建了一个对象,只有当用delete进行释放该对象的时候,析构函数才会被调用;
析构函数的作用:
先拿构造函数来说话,构造函数是新建对象吗?回答:不是,而是在对象被创建出来之后自动被调用的,用来初始化相关信息的函数。同理,析构函数也不是用来删除对象的,而是当对象被删除的时候自动会被调用的,用来做一些对象被删除之前的清理工作。只要对象的生命周期结束,那么程序就会自动执行析构函数来完成这个工作的。
析构函数的特点:
析构函数不返回任何值,没有函数类型,也没有任何函数的参数。由于上面这些特点,所以析构函数不能被重载,所以说一个类可以有多个构造函数,但只能有一个析构函数。
#include <iostream> using namespace std; class Student{ public: int age; Student(){std::cout << "101" << std::endl;} Student(int age){ this->age = age; std::cout << "你好" <<age<<"岁了"<< std::endl; } ~Student(){cout<<"我是析构函数,程序结束前时调用"<<endl;} }; int main() { Student stu0(60);//单个调用有参数的构造函数 Student stu1[3]={Student(30),Student(30),Student(30)};//数组对象调用有参数的构造函数 }
3.拷贝构造函数
-
使用一个已经创建完毕的对象来初始化一个新对象
-
值传递的方式给函数参数传值
-
以值方式返回局部对象
class Person { public: Person() { cout << "无参构造函数!" << endl; mAge = 0; } Person(int age) { cout << "有参构造函数!" << endl; mAge = age; } Person(const Person& p) { cout << "拷贝构造函数!" << endl; mAge = p.mAge; } //析构函数在释放内存之前调用 ~Person() { cout << "析构函数!" << endl; } public: int mAge; }; //1. 使用一个已经创建完毕的对象来初始化一个新对象 void test01() { Person man(100); //p对象已经创建完毕 Person newman(man); //调用拷贝构造函数 Person newman2 = man; //拷贝构造 //Person newman3; //newman3 = man; //不是调用拷贝构造函数,赋值操作。因为newman3是先存在后,再赋值的,此时只是简单的赋值操作,初始化操作时才会调用拷贝构造函数 } //2. 值传递的方式给函数参数传值 //相当于Person p1 = p; void doWork(Person p1) {} void test02() { Person p; //无参构造函数 doWork(p);//调用了拷贝构造函数,参数p为上面p的副本 } //3. 以值方式返回局部对象 Person doWork2() { Person p1; cout << (int *)&p1 << endl; return p1;//此时返回的值是Person P1的一个副本里保存的值。是调用了拷贝构造函数重新形成的值,是拷贝了p1,形成副本 } void test03() { Person p = doWork2(); cout << (int *)&p << endl; } int main() { //test01(); //test02(); test03(); system("pause"); return 0; }
归根到底就是,只要进行了初始化赋值操作就会调用拷贝构造函数。
重点:一般编译器初始化形成的拷贝函数只会进行浅拷贝,这样在使用拷贝操作时由于进行了浅拷贝(简单的赋值操作),在结束时由于析构函数会释放空间,会引发一些问题,比如指向同一地址的指针会进行多次析构,而造成出错。
从而编译器自己形成的浅拷贝的构造函数一般是不用的,而是自己进行复写拷贝函数,解决浅拷贝多次调用析构函数后形成的内存错误问题,自己写的构造函数称为深拷贝构造函数。
**深拷贝与浅拷贝
class Person { public: //无参(默认)构造函数 Person() { cout << "无参构造函数!" << endl; } //有参构造函数 Person(int age ,int height) { cout << "有参构造函数!" << endl; m_age = age; m_height = new int(height); } //深拷贝构造函数 Person(const Person& p) { cout << "拷贝构造函数!" << endl; //如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题 m_age = p.m_age; m_height = new int(*p.m_height);//重新申请内存,避免浅拷贝的简单赋值 //系统默认生成的是m_height = m_height,简单赋值操作的浅拷贝,因为m_height是指针,此时会简单进行地址赋值操作,析构时会多次释放同一地址 } //析构函数 ~Person() { cout << "析构函数!" << endl; if (m_height != NULL) { delete m_height; } } public: int m_age; int* m_height; }; void test01() { Person p1(18, 180); Person p2(p1); cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl; cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl; } int main() { test01(); system("pause"); return 0; }