title: CPP中构造函数的分类以及调用(容易出错)
date: 2020-05-04 16:38:54
tags:
- C++
- 面向对象
categories: c/c++
description: 关于C++的个人学习心得。在面向对象编程中,对析构函数的调用新手很容易出现迷惑,因为C++的析构函数是由分类和多种调用方式的,本文通过代码演示析构函数的分类和调用的知识点
析构函数的分类以及调用
直接上代码看知识点:
/*
析构函数的分类和调用
两种分类方式:
·按参数分:有参构造和无参构造(默认构造)
·按类型分:普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
*/
#include <iostream>
using namespace std;
//分类
class Person
{
public:
//构造函数
Person()
{
cout << "Person的无参构造函数调用" << endl;
}
Person(int a)
{
age = a;
cout << "Person的有参构造函数调用" << endl;
}
//拷贝构造函数
Person( const Person &p)
{
//将传入的人身上的所有属性,拷贝到我身上
age = p.age;
cout << "Person的拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person的析构函数调用" << endl;
}
int age;
};
//调用
void test01()
{
//1、括号法
Person p;///默认构造函数调用
Person p2(10); //有参构造函数调用
Person p3(p2); //拷贝构造函数的调用
//注意事项1:
// ·调用默认构造函数的时候不要加(),因为编译器会认为是一个函数的声明!不会认为在创建对象
Person p4(); //不会创建对象,编译器以为是一个函数声明了!
//2、显式调用
Person p5;
Person p6 = Person(10); //显式的告诉编译器调用的是有参构造
Person p7 = Person(p6); // 拷贝构造
Person(10); //匿名对象 特点:当前行执行结束后,系统会立即回收掉匿名对象
cout << "aaa" << endl; //可以看到上面的匿名函数会触发构造和析构后才执行这里的打印操作,印证了上面所说
//注意事项2:
// ·不要利用拷贝构造函数,初始化匿名对象
//Person(p6); //报错,编译器会认为Person(p6) -> Person p6,认为这是一个对象的声明
//3、隐式调用
//相当于 Person p8 = Person(10);隐式的给你转换成显式法,只能用于调用单个参数的构造函数
Person p8 = 10;
}
int main()
{
test01();
system("pause");
return 0;
}
总结注意事项:
- 调用默认构造函数的时候不要加(),因为编译器会认为是一个函数的声明!不会认为在创建对象
- 不要利用拷贝构造函数,初始化匿名对象。编译器会认为这是一个对象的声明
构造函数调用规则
默认情况下,c++编译器至少给一个类添加3个函数(在我们没有提供它们的时候):
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝(默认会对所有成员属性进行拷贝)
构造函数的调用规则:
- 如果用户定义有参构造函数,c++不在提供无参构造,但是会提供默认构造
- 如果用户定义拷贝函数,c++不会再提供其他构造函数
补充一个构造函数与析构函数的知识点:
- 当其他类对象作为本类成员,构造时候先构造类对象,再构造自身。而析构的顺序和构造的顺序是相反的!
深拷贝和浅拷贝
这是构造函数里面作为面试题经常考察的知识点,这里做一个拓展知识简单说一下。
浅拷贝:简单的赋值拷贝操作
浅拷贝就是我们默认的拷贝构造函数所作的简单的对所有成员属性进行赋值操作。
浅拷贝带来的问题是堆区的内存重复释放
上述的浅拷贝问题,要使用深拷贝进行解决。
深拷贝:在堆区重新申请空间,进行拷贝操作
所谓深拷贝,就是我们自己去提供拷贝构造函数,在拷贝函数中给在堆空间的成员属性重新开辟一块堆空间,然后将数据放到新创建的堆内存上。
解决问题的本质就是:不让这两个对象的在堆空间创建的成员指针属性指向同一块地址。
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题