-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreadme copy.txt
More file actions
112 lines (104 loc) · 7.47 KB
/
readme copy.txt
File metadata and controls
112 lines (104 loc) · 7.47 KB
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
chapter_13: 拷贝构造函数,拷贝赋值运算符,析构函数, =default已经=delete,private阻止拷贝,拷贝控制
自定义swap,对象移动
chapter_13 NOTICE:
/* 拷贝构造函数 */
1. class_name( const class_name&) 注意是引用型
2. class a=b; 触发 非引用形参 触发 返回非引用类型 触发, 向容器push_back对象时也会触发
所以说容器内的对象不会影响原来用来赋值的那个对象;但是emplace不一样
3. 使用默认的合成构造函数,如果类内有指针成员,会造成两个实例内成员指向同一个内存(潜复制),
调用析构函数时会清空两次内存引发异常
4. 合成的会将非static成员拷贝在正在创建的对象中
/* 拷贝赋值运算符 */
1. class_name& operator=(const class_name&)
2. a=b时触发(已经初始化后使用赋值运算符)
3. 赋值运算符返回左侧运算对象的引用
/*析构函数*/
1. 成员是指针,析构函数不会delete该指针所指向的对象
2. 触发条件: 离开作用域、delete手动销毁、容器被销毁时自动删除其元素、临时对象
3. 对象的引用、指针离开作用域时不会触发析构函数
下面这种情况会触发析构:
HasPtr f(HasPtr hp){
HasPtr ret=hp;
return ret;
}
f返回时,hp和ret都被销毁,如果HasPtr有指针成员,且这几个对象包含相同的指针值就会出现错误
/* 什么时候需要这些函数 */
1. 如果一个类需要自定的析构函数,那么他一定需要拷贝构造函数和拷贝赋值运算符
2. 如果一个类需要拷贝构造函数,那么一定需要拷贝赋值运算法,反之亦然
/* =default */
1. 显示要求使用析构/拷贝构造/拷贝赋值函数:
Hasptr(const Hasptr& ) =default
~HasPtr() =default
Hasptr& operator=(const Hasptr& ) =default
2. =default是内联函数,如果不想要内联,就在类外定义
/* =delete */
1. 阻止赋值,作用就是提醒我们不希望定义这些成员!
Hasptr& operator=(const Hasptr& ) = delete
2. 其他函数也能用=delete,但是析构函数不要删除,否则就无法销毁对象了
3. 合成的函数可能是删除的,其规则是:
这个类里面的成员不能构造、拷贝或者销毁(比如析构函数是privete
或者是被删除了的)
如果有const成员,则拷贝赋值运算符是删除的(因为const不可能被
赋值)
/* 拷贝控制 */
/* 自定义swap */
这里知道swap自定义,交换指针指向的而不用临时变量,但是std::swap那里没懂
13.4 13.5 看不懂
/* 对象移动 */
1. 右值、左值引用:
右值引用 : int && a=4 (yes), int &&a =b (no), int &&a = b*3(yes)
int && a=move(b) (yes)
2. 意义:例如vector重新分配内存,从旧内存上面 拷贝到新内存很费时间且不必要,更好
的方式是采用移动
3. 使用方式:
Foo (const Foo && a): m1(a.m1), m2(a.m2) {delete a.m1; delete a.m2;}
chapter_15: 继承,动态绑定,基类和派生类,虚函数,final, 抽象基类,
chapter_15 NOTICE:
/* 继承 */
/* 基类和派生类 */
基类定义:
1. 定义一个虚析构函数
2. 有虚函数 virtual修饰
虚函数继承,动态绑定只有在指针和引用时触发,且在运行时才会被解析(不是编译的时候)
派生类:
class Deriv : public Base{
};
1. public是访问控制说明符,有public和private,protected,第三种好像没什么用
2. 重写虚函数的时候最好加上override说明是覆盖
3. 成员函数加上const是修饰 *this指针,不让该函数修改成员的值
4. 基类指针指向基类或者派生类,派生类可以向基类转换,但是基类不能转派生类(使用static_cast强转可以)
5. 派生类构造函数,关于基类成员,应该使用基累的构造函数来初始化(使用基类接口初始化基类成员)
6. 静态成员只有唯一实例
7. 派生类的声明不应该出现派生列表:
class Deriv; 是对对
class Deriv : public Base是错的
具体定义那儿在指明派生列表
8. 想使用某个基类来派生,那么该基类一定要被定义,而不只是被声明
9. 使用 final 禁止类被继承:
class NoDeriv final {/* ... */ };
10. 静态类型与动态类型:静态类型就是直接声明一个对象(不用指针指向,这时候不会发生动态绑定!),
而且派生类向基类转换只对指针或者引用有效
11. 切掉: 用派生类对某个对象,来初始化基类的对象,这样做会把派生类的某些成员忽略掉,称之为切掉
/* 虚函数 */
1. 定义: virtual func(); 在派生类中,可以在前面加 virtual,也可以不加,但是后面最好加上
override: func() override, 这样编译的时候会检查是不是正确的虚函数
2. 虚函数必须被定义
3. 对虚函数的调用可能在运行时才被解析
4. 多态: 虚函数,动态绑定
5. 动态绑定,通过基类指针或引用调用虚函数的时候,采用的是基类的虚函数的默认形参,所以派生类的默认
形参最好和基类中虚函数的默认形参一致!
6. final修饰的函数,不可以被派生类覆盖
7. 想使用基类的虚函数,而不是动态绑定,使用 作用域运算符:
Base::finc();
/* 抽象基类 */
1. 定义方式: 通过纯虚函数:
virtual func() = 0;
2. 抽象基类不能被实例化,其派生类应该覆盖掉虚函数(必须实现每个虚函数)
/* 访问控制与继承 */
1. protected: 只能被派生类访问,而不能被其他访问
2. 派生类中的友元:只能访问派生类的私有成员,不能访问基类的protected和私有成员
3. public继承与private继承:
他们都不能访问基类中的私有成员,public继承会继续保留基类成员的性质(public, protected)
private继承:在派生类中,基类成员都变成类private的,也就是类里面可以直接访问,但是外面不能
访问基类的成员类
/* 拷贝构造函数 */