每日总结

今天跟着视频学习了拷贝函数的基本用法,同时对视频中的案例都进行了代码实现,有一些需要理解的知识点如下。

学习了拷贝函数的基本构造语法:

Person(const Person &p){}

学习了拷贝函数的调用方法,调用时机:

  • 使用一个创建完毕的对象来调用拷贝一个新的对象,默认属性全部复制:
    1
    2
    Person p1(10) ;
    Person p2(p1) ;
  • 通过值传递给参数赋值
    1
    2
    3
    4
    5
    void doWork(Person p){}
    void test01{
    Person p;
    doWork(p);
    }
  • 通过调用函数,利用值的方式返回局部对象
    1
    2
    3
    4
    5
    6
    7
    8
    Person doWork(){
    Person p1;
    return p1;
    }
    void test02(){
    Person p = doWork();
    }

学习了调用规则:

  • 如果类中没有写任何对象初始化和清理的函数,那么编译器会默认调用默认构造函数(无参,函数体为空),默认析构函数(无参,函数体为空),默认拷贝构造函数(对属性值进行拷贝);
  • 如果类中定义了含参数的构造函数,编译器会提供默认拷贝函数和析构函数,不会再提供没有参数的构造函数;
  • 如果类中定义了拷贝构造函数,那么编译器不会再提供其他构造函数。

学会了深拷贝和浅拷贝的概念

一般编译器会默认拷贝构造函数为浅拷贝,即复制所有的属性,这样的情况下如果传入的是一个指针,那么拷贝出来的属性就会指向同一个地址,如果删除了前一个指针指向的堆空间,那么拷贝出来的指针就会指向空,引起出错,这就是典型的堆区重复使用

  • 浅拷贝:简单的赋值拷贝
  • 深拷贝:申请堆空间进行拷贝
  • 总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<iostream>
using namespace std;
class Person{
public:
Person(int age,int height){
m_age = age;
m_height = new int(height);
cout<<"Person 含参数构造成功!"<<endl;
}

Person(const Person &p){
cout<<"拷贝函数!"<<endl;
m_age = p.m_age;
// m_height = p.m_height;
/*编译器提供这两行代码,但是在析构函数中,会先删除p的m_height指向的空间,而 p1的m_height
也指向这个空间,导致非法操作,因此需要在拷贝函数中添加一些操作防止指向同一个空间
*/
m_height = new int(*p.m_height); //不要忘记解引用哦~

}
~Person(){
cout<<"Person 析构函数"<<endl;
if(m_height != NULL){
delete m_height;
m_height = NULL;
}
}
public:
int m_age;
int *m_height;
};

void test(){
Person p(21,173);
cout<<"p年龄:"<<p.m_age<<" 身高:"<<*p.m_height<<endl;
Person p1 = p;
cout<<"p1年龄:"<<p1.m_age<<" 身高:"<<*p1.m_height<<endl;
}
int main(){
test();
system("pause");
return 0;
}