C++ 类和结构体

调用顺序

如果一个类里面存在其他类的属性对象,那么构造函数和析构函数的调用顺序为:

创建时:先调用属性对象的构造函数,在执行自己的构造函数;
销毁时:先调用自己的析构函数,再执行属性对象的析构函数;

构造函数

构造函数在对象被销毁时调用;

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
class Student{
private:
char* name;
int age;
public:
// 无参构造函数(会覆盖父类的无参构造函数)
Student(){
cout << "调用无参构造函数" << endl;
this->name = (char*)malloc(sizeof(char)*100);
strcpy(name, "json");
}

// 定义有参的构造函数后,默认无参构造函数将被覆盖
Student(int age, char* name){
// 字符串后面会带有0作为结束符,所以要加1
int len = strlen(name);
this->name = (char*)malloc(len + 1);
strcpy(this->name, name);

this->age = age;
}
};

void main(){
// 只要声明变量,就会调用无参构造函数
//Student s;

// 调用有参构造函数
//Student s1(20, "lili");
//Student s3 = Student(20, "Damn");

system("pause");
}

析构函数

析构函数在对象被销毁时调用;

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
class Student{
private:
char* name;
int age;
public:
// 无参构造函数(会覆盖父类的无参构造函数)
Student(){
cout << "调用无参构造函数" << endl;
this->name = (char*)malloc(sizeof(char)*100);
strcpy(name, "json");
}

// 定义有参的构造函数后,默认无参构造函数将被覆盖
Student(int age, char* name){
// 字符串后面会带有0作为结束符,所以要加1
int len = strlen(name);
this->name = (char*)malloc(len + 1);
strcpy(this->name, name);

this->age = age;
}

// 对象被销毁时调用,可以做善后处理,比如,释放内存
~Student(){
cout << "调用析构函数" << endl;
free(name);
}
};

void func(){
Student t;
}

void main(){
//func();
system("pause");
}

拷贝构造函数

调用场景:
1,变量间的赋值: T t1 = t2;
2,函数调用时传参,值传递时调用;
3,作为函数返回值返回,给其他变量赋值时(与第1种情况同 )

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class Student{
private:
char* name;
int age;
public:
// 无参构造函数(会覆盖父类的无参构造函数)
Student(){
cout << "调用无参构造函数" << endl;
this->name = (char*)malloc(sizeof(char)*100);
strcpy(name, "json");
}

// 定义有参的构造函数后,默认无参构造函数将被覆盖
Student(int age, char* name){
// 字符串后面会带有0作为结束符,所以要加1
int len = strlen(name);
this->name = (char*)malloc(len + 1);
strcpy(this->name, name);

this->age = age;
}

// 对象被销毁时调用,可以做善后处理,比如,释放内存
~Student(){
cout << "调用析构函数" << endl;
free(name);
}

// 拷贝构造函数
// 调用场景:
// 1,变量间的赋值: T t1 = t2;
// 2,函数调用时传参,值传递时调用;
// 3,作为函数返回值返回,给其他变量赋值时(与第1种情况同 )
// 默认情况下,拷贝构造函数是浅拷贝(值拷贝)
//Student(const Student &obj){
// this->name = obj.name;
// this->age = obj.age;
// cout << "copy obj" << endl;
//}

// 深拷贝
// 开辟一段新的内存,不只拷贝变量值,还会拷贝对应的地址内容
Student(const Student &obj){
int len = strlen(obj.name);
// 字符串后面会带有0作为结束符,所以要加1
this->name = (char*)malloc(len + 1);
strcpy(this->name, obj.name);
this->age = obj.age;
}
};

void main(){

Student s(30,"lilian");
Student s_cp = s;

copyPro();

system("pause");
}
浅拷贝问题

默认情况下,拷贝构造函数是浅拷贝,也就是值拷贝。当我们在构造函数中为成员变量开辟了动态
堆内存空间,进行浅拷贝后,拷贝变量也会指向同一个堆内存空间,此时,在变量销毁时,调用
析构函数,源变量和拷贝变量都会对同一内存空间进行释放,此时就会报错。
(浅拷贝是值拷贝,而指针的值是地址,所以拷贝出来都是指向同一个地址空间)

1
2
3
4
5
6
7
8
void copyPro(){
Student stu(10, "llll");

// stu2拷贝了stu的变量值,其成员变量name同时指向了同一个内存空间
Student stu2 = stu;

// 函数销毁时,stu和stu2都会调用析构函数去释放内存空间,导致重复释放的问题
}

深拷贝解决浅拷贝问题:
开辟一段新的内存,不只拷贝变量值,还会拷贝对应的地址内容;

1
2
3
4
5
6
7
Student(const Student &obj){
int len = strlen(obj.name);
// 字符串后面会带有0作为结束符,所以要加1
this->name = (char*)malloc(len + 1);
strcpy(this->name, obj.name);
this->age = obj.age;
}