-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCPlusBase1.cpp
272 lines (245 loc) · 8.65 KB
/
CPlusBase1.cpp
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
//
// Created by zenghui on 1/4/21.
//
#include <iostream>
#include "CPlusBase1.h"
using namespace std;
/**
* C++构造函数与析构函数
* 什么是构造函数:
* 构造函数是一种特殊的成员函数,与其他函数不同,不需要用户调用它,而是创建对象的时候自动调用。
* 什么是析构函数:
* 析构函数是对象不再使用的时候,需要清理资源的时候调用。C++编译器自动调用。
*/
class Parent {
public:
int age;
char *name = NULL;
//无参构造
Parent() {
cout << "parent 调用无参构造函数" << endl;
};
/**
* 参数列表写法
* 即:
* 将ageValue赋值给age
* 将heightValue赋值给height
* @param age
* @param height
*/
Parent(int ageValue, char *nameValue, int heightValue) :
age(ageValue),
name(changeName(nameValue)),
height(heightValue) {
}
//单参构造
Parent(int age) {
cout << "parent 调用单参构造函数" << endl;
this->age = age;
}
//单参构造
Parent(int age, char *name) {
cout << "parent 调用单参构造函数" << endl;
this->age = age;
this->name = changeName(name);
}
/**
* explicit 关键字:
* 被explicit关键字修饰的类构造函数,不能进行自动地隐式类型转换,只能显式地进行类型转换。
* @param name
*/
explicit Parent(char *name) {
cout << "parent 调用多参构造函数" << endl;
this->name = changeName(name);
}
/**
* 默认拷贝函数
* @param parent 常量引用(防止原有值被修改)
* 常量引用指向的内容不能修改,但是能重新赋值
* 引用常量指向的内容能修改,但是不能修改
* 注意:
* 如果重写该方法必须对属性进行赋值,赋值会导致属性值为空
*
* 浅拷贝:
* 所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的
* 也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就
* 会出问题了.
*
*
* 深拷贝:
*
*/
Parent(const Parent &parent) {
/**
* 浅拷贝演示,当存在malloc的时候会出现报错。
* 出现报错原因是由于浅拷贝是 将原有对象属性name的值赋值给新对象属性name。
* 当新对象被释放时,name属性指向的堆中的数据被释放。
* 而原有的对象属性name的指针还存在。
* 当系统调用析构的时候释放原有对象时,由于name的指向被新对象释放掉了,系统调用的时候会再次去释放name对象从而二次释放的情况。出现报错
*/
// this->age = parent.age;
// this->name = parent.name;
// this->height = parent.height;
/**
* 深拷贝:
* 当有对象在堆内存申请空间了,那就需要深拷贝。
* 为了解决浅拷贝出现的问题.
* 解决办法:
* 重新对新对象开辟一个指向。释放的时候释放自己的,不在对原有对象的属性指向进行释放
*/
cout << "parent 调用默认拷贝函数 深拷贝" << endl;
this->age = parent.age;
if (parent.name) {
this->name = changeName(parent.name);
}
this->height = parent.height;
cout << "parent 调用默认拷贝函数" << endl;
}
/**
* 重写拷贝函数(存在风险,当里面对值进行修改,外面也会被修改)
* java中使用的是这种
* @param parent
*/
Parent(Parent &parent) {
/**
* 对数据进行拷贝
*/
this->age = parent.age;
parent.age = 100;
if (parent.name) {
this->name = changeName(parent.name);
}
this->height = parent.height;
cout << "parent 调用重写拷贝函数" << endl;
}
/**
* 参数列表可以传递方法返回值
*/
char *changeName(char *nameValue) {
/**
* 给name值申请堆内存。
* 测试浅拷贝与深拷贝
*/
char *p = (char *) malloc(strlen(nameValue));
strcpy(p, nameValue);
return p;
}
//析构函数
~Parent() {
cout << "parent 调用析造函数" << endl;
if (this->name != NULL) {
free(this->name);
this->name = NULL;
}
}
private:
int height;
};
void testParent(Parent parent) {
cout << "testParent parent age: " << parent.age << endl;
}
//void testParent(Parent &parent) {
//
//}
/**
* 测试方法
*/
void testCPlus1() {
/**
* 1. 括号法创建实例
*/
Parent p2(10);
/**
* Parent parent;
* 对象已经在栈中创建
* 并且能对该对象调用,或者赋值。
*/
Parent parent;
parent.age = 18;
cout << "输出parent对象值:" << parent.age << endl;
/**
* 2. 隐式法创建,注意隐式调用无法调用多参构造(使用比较少,容易产生歧义)
*/
Parent parent1 = 10;
/**
* explicit 修饰的构造方法无法用隐式的方法调用,只能使用显示的方法调用
* 显示:
* Parent parent2 = Parent(p);
* 隐式:
* Parent parent2 = p;
*/
char a = 'a';
char *p = &a;
// Parent parent2 = p;
Parent parent2 = Parent(p);
/**
* 3. 显示法创建实例
*/
Parent p4 = Parent();
/**
* 4. 调用默认的拷贝函数
*/
Parent parent3 = Parent(parent2);
/**
* 当数组创建时,对象会创建4个并且都会进行初始化
*/
Parent pArr[4];
}
void testCPlus2() {
cout << "创建案例结束,开始传参案例" << endl;
/**
* 输出:
* parent 调用单参构造函数
* parent 调用析造函数
* parent 调用析造函数
* 问题来了:
* 为什么调用testParent(Parent p)会两次析构函数
* void testParent(Parent p)
* 当调用testParent(Parent p)方法是,C++会调用Parent默认的拷贝函数即:
* Parent(const Parent &parent)
* 对p进行初始化。即:
* Parent p = Parent(const Parent &parent)。
* 当存在重写的
* Parent(Parent &parent)
* 会调用重写的方法对p进行初始化。即:
* Parent p = Parent(Parent &parent)。
* 当testParent栈帧被销毁的时候,会调用P也就出现了开始只调用了一次构造函数却出现了两次析构函数的问题。
*
* 为什么调用testParent(Parent &p)只会析构函数
* void testParent(Parent &p)由于采用的是引用的方法,引用指向的是原有的变量,不会重新创建一个新的变量。
* 浅拷贝:
* 所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的
* 也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就
* 会出问题了.比方说出现malloc
* 深拷贝:
*
*/
Parent p5 = Parent(101, const_cast<char *>("zenghui"));
testParent(p5);
/**
* 当重写了Parent(Parent &parent),导致数据被修改
*/
cout << "当重写了Parent(Parent &parent),导致数据被修改 parent age: " << p5.age << endl;
/**
* malloc和new却别:
* 所有new出来的对象,都是返回该类型的指针。
* malloc返回的void *,需要手动强转
* malloc不会调用构造方法,new会调用构造方法
* new是运算符,malloc是方法
* 都需要手动去释放内存
* new出来的对象需要用delete进行释放,malloc出来的对象需要用free进行释放
* new不会失败,malloc有可能会出现失败的情况
* new对象用 void * 取接受,释放不了对象
* malloc缺点:
* 1. 程序员必须确定对象的长度
* 2. malloc 返回一个 (void *) 指针 ,c++不允许将 (void*) 赋值给其它指针,必须强转
* 3. malloc可能申请内存失败,所以必须判断返回值来保存内存分配成功
* 4. 用户在使用对象之前必须记住对他初始化,构造函数不能显示调用初始化(构造函数是由编
* 译器调用的),用户有可能忘记调用初始化函数
*/
cout << "演示new数组释放" << endl;
Parent *pArray = new Parent[3];
delete[]pArray;
pArray=NULL;
cout << "释放new数组" << endl;
}