Skip to content

Commit ed74f1f

Browse files
committed
fix (learncpp series): disable draft
1 parent 012fd5c commit ed74f1f

File tree

7 files changed

+204
-11
lines changed

7 files changed

+204
-11
lines changed

content/posts/about_swap_on_linux/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
+++
22
date = '2025-11-01T21:57:31+08:00'
3-
draft = true
3+
draft = false
44
title = '关于Linux的swap那些事情'
55
+++
66

content/posts/learncpp_1/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ statement expression(原谅我不会翻译):只含一个表达式的语句
8181
这个关键字的意思,指的是能够在编译时就确定常量(甚至函数),而不是运行时,从而把运行时开销转换成编译时间。
8282

8383
现在,对于简单的函数,不含`new/delete``cin/cout`,随机数生成的函数,我们都推荐使用`constexpr`
84+
85+
需要注意的是,如果一个常量是在块作用域中,而且初始化用的是常量表达式(`{}` *零初始化* 是常量表达式),那么它会自动成为常量表达式。
8486

8587
需要注意的是,在C++11~20的不同版本中,支持的类型略有差异,比如后期才支持流程控制的for/while/if。
8688

content/posts/learncpp_2/index.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ magic number这个词,我一直认为是大家约定好了的一组数,但
2424

2525
怎么说呢,感觉比c先进一点点(?
2626

27-
## string_view
27+
### string
28+
29+
因为`string`本身就是一个类,其构造函数本身就是重载的,所以无论在初始化的时候传入`string`,`C-Style string`还是`string_view`,都不会发生隐式转换。
30+
31+
但是如果函数要求的只是一个`string`参数,那么把别的类型传入,就会发生编译错误。原因是不允许发生`string_view``string`的窄化转换,性能问题。
32+
33+
### string_view
2834

2935
```cpp
3036
// 用string_view指向string的话,不产生多余的拷贝,只是在初始化的时候拷贝进入string
@@ -52,7 +58,7 @@ std::string_view name {"John"};
5258
对于用`string_view`来说,推荐用法:
5359
5460
```cpp
55-
std::string_view s{"aaa"sv};
61+
std::string_view s{"aaa"sv}; //在堆上分配了C-Style字符串,但是用的构造函数不一致,在复制省略中,这是一种过编译的办法。
5662
```
5763

5864
实现零成本抽象,原因嘛,以后再补充...
@@ -129,7 +135,7 @@ int main()
129135
130136
总结下,`inline`除了内联的意义,其实还有:
131137

132-
1. 对于变量: 在头文件中定义,使得此头被多次引入的时候只使用共享的一次,避免每次都加载到文件,在头文件被多次引用的时候很有用(c++17)。
138+
1. 对于变量: 在头文件中定义,使得此头被多次引入的时候只使用共享的一次,避免每次都加载到文件,在头文件被多次引用的时候很有用(c++17)。同时,如果变量同时被`static constexpr`修饰,那么它是隐式`inline`的。
133139
2. 对于函数:在头文件中定义的函数需要加(但是不推荐,等到后面看到类的构造函数再会来补充),声明的函数无须加。
134140
3. 对于命名空间: 把某个命名空间中的内容暴露到全局的命名空间,使得其中内容访问无须加域解析操作符。
135141

content/posts/learncpp_4/index.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@ int foo(int a) {
3333
1. 如果使用`foo()`,那么会直接调用非模板的函数。
3434
2. 如果用`foo<>()`,那么会自动推断,并从模板实例化函数。如果指定类型,则直接从模板按指定的类型实例化函数。
3535

36-
在两个不同的模板参数时,可以定义模板返回用`auto`关键字,让编译器自动推导返回类型。
36+
在两个不同的模板参数时,可以定义模板返回用`auto`关键字,让编译器自动推导返回类型。这是C++17后的CTAD(class template auto deduction)。
37+
38+
> 注意:
39+
> CTAD仅用于推断实参(argument,调用时)
40+
> 不能推断形参(parameter,函数声明)
3741
3842
在C++20后,函数参数传入`auto`会自动变成模板。
3943

4044
---
4145

42-
非类型模板:定义`constexpr`或者`consteval`(C++20)。
46+
非类型模板:定义`constexpr`或者`consteval`(C++20),不写 `typename`
4347

4448
```cpp
4549
template <int N>
@@ -127,3 +131,6 @@ constexpr const auto& ref{getConstRef()};
127131
128132
因为指针是一个对象,而引用可以理解成一个别名,所以当我们对引用进行推断的时候,实际上是推断原始的对象究竟是什么类型。
129133
134+
### in/out 参数
135+
136+
为了避免拷贝,可以预先初始化一个对象传入其引用,这样可以避免拷贝,不过使得参数列表很乱。如果可行,还是通过返回值优化或者移动语义。

content/posts/learncpp_5/index.md

Lines changed: 181 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
+++
22
date = '2025-09-29T08:25:14+08:00'
3-
draft = true
4-
title = 'Learncpp_5'
3+
draft = false
4+
title = 'cpp学习笔记(5)'
55
+++
66

77
## 枚举
@@ -159,4 +159,182 @@ int main()
159159

160160
> 为什么`getter`不能像上面一样反过来?
161161
162-
因为在类内部的存储对象就是真正的`string`,给出它的引用使得外部访问的类型明确,不会造成歧义;同时也提示这个引用的生命周期是明确的————跟成员变量一致。
162+
因为在类内部的存储对象就是真正的`string`,给出它的引用使得外部访问的类型明确,不会造成歧义;同时也提示这个引用的生命周期是明确的————跟成员变量一致。
163+
164+
### 构造函数
165+
166+
构造函数用于初始化类,在此我们可以手动指定成员变量如何初始化。
167+
168+
#### 转化构造函数
169+
170+
转化构造函数其实就是特殊的构造函数,其只拥有一个参数。如果传入一个该参数类型的变量,那么就会自动把这这个变量转化成类。
171+
172+
#### 默认构造函数
173+
174+
```cpp
175+
class Foo{
176+
int m_a;
177+
int m_b{};
178+
179+
public:
180+
Foo () = default; //可以显式的声明构造函数为默认构造函数,此时m_a会被默认初始化为0。
181+
Foo () {} //用户自己初始化,但是空初始化,此时m_a不会被初始化,是垃圾值。
182+
}
183+
```
184+
185+
#### 委托构造函数
186+
187+
在一个构造函数的`:`后面写上参数更多的重载构造函数,可以用另一个构造函数来初始化,但是此构造函数就不能再初始化成员变量。
188+
189+
尽量不要使用太多的构造函数。
190+
191+
#### 复制构造函数
192+
193+
复制构造函数的参数必须是引用,要不然就会发生无限递归的调用复制函数。
194+
195+
复制构造函数不应用于复制以外的意图,因为编译器可能会发生复制省略(copy elision)
196+
197+
### explicit关键字
198+
199+
禁止隐式转换:从单个参数隐式转换成类对象;从列表`{xxx,xxx}`转换成类对象。
200+
201+
在具有单个参数的构造函数前加入,避免编译器执行隐式转换。
202+
203+
由于C++对于用户定义的转换,只允许转换一次,所以下面的代码会报错:
204+
205+
```cpp
206+
#include <iostream>
207+
#include <string>
208+
#include <string_view>
209+
210+
class Employee
211+
{
212+
private:
213+
std::string m_name{};
214+
215+
public:
216+
Employee(std::string_view name)
217+
: m_name{ name }
218+
{
219+
}
220+
221+
const std::string& getName() const { return m_name; }
222+
};
223+
224+
void printEmployee(Employee e) // has an Employee parameter
225+
{
226+
std::cout << e.getName();
227+
}
228+
229+
int main()
230+
{
231+
// 此时要经历两次转换:C-Style string -> string_view -> class Employee
232+
printEmployee("Joe"); // compile error
233+
234+
// 修正:
235+
// 方法1:
236+
// using std::literals;
237+
// printEmployee("Joe"sv);
238+
// 方法2:
239+
// printEmployee(Employee{"Joe"});
240+
241+
242+
return 0;
243+
}
244+
```
245+
246+
不对复制/移动构造函数使用`explicit`,因为他们不执行隐式转换。
247+
248+
如果转换的时候两者等效且零开销,可以不使用`explicit`
249+
250+
比如:
251+
252+
1. `const char*` -> `string_view`
253+
2. `string` -> `string_view`
254+
255+
### constexpr问题
256+
257+
从C++14开始,`constexpr`修饰函数仅仅是作为编译时求值的提示,如果传入的变量不是一个`constexpr`,那么这个函数就具有运行时的上下文,`constexpr`修饰就不起作用。
258+
259+
对于`struct`,其作为一个聚合体,默认的构造函数无须加入`constexpr`就可以用它初始化一个类对象。但是对于`class`,就必须`public`,同时手动指定构造函数是`constexpr`,比如:
260+
261+
```cpp
262+
Foo {
263+
public:
264+
constexpr Foo = default;
265+
}
266+
```
267+
268+
`constexpr`修饰一个类对象的时候,如果涉及到的函数(构造函数、`setter`以及使用到的成员函数)都有`constexpr`修饰,表示这个类具有编译时的上下文,会让此对象成为一个编译时常量。在编译时如果此对象是用临时对象初始化的,对临时对象是可以修改的,但是一旦这个对象初始化完毕,那么就不再可以修改。
269+
270+
后续要访问此`constexpr`对象,那么所有的函数`()`后,都必须有`const`,保证不修改此对象。
271+
272+
参考(learncpp - 14.17)[https://www.learncpp.com/cpp-tutorial/constexpr-aggregates-and-classes/]
273+
274+
### this指针
275+
276+
`this`指针是一个`const`指针(顶层),指向当前操作的类。
277+
278+
`this`指针出现比引用早,不然它多少是个引用。
279+
280+
### 成员函数类外定义
281+
282+
成员函数可以在类外定义,要加上域访问解析符`::`。如果是类的正下方(`.h`中),前面加`inline`关键字以防止重复包含;如果是对应的cpp中,无须加`inline`
283+
284+
默认参数在声明时给出。
285+
286+
### 类型模板参数
287+
288+
可以在类指定类型模板参数,不过如果成员函数先声明,然后在类外定义,那么需要单独再指定一次模板参数。
289+
290+
模板类外的成员函数要紧挨着类定义的下面。
291+
292+
注意:
293+
294+
1. 所有的模板函数都是默认内联的,所以就算在类外定义,已经隐式`inline`了。
295+
2. 类外定义的函数的类型模板参数必须与类的一致。
296+
297+
传入类模板的参数,无须加`<T>`,因为已经在`Pair<T>::`作用域中了。
298+
299+
```cpp
300+
template <typename T>
301+
bool Pair<T>::isEqual(const Pair& pair) // note the parameter has type Pair, not Pair<T>
302+
{
303+
return m_first == pair.m_first && m_second == pair.m_second;
304+
}
305+
```
306+
307+
### 静态成员变量
308+
309+
静态成员变量在所有实例化的对象都可用,具有相同的值,在未实例化的时候也可以使用,直接用类名和域访问解析符访问。
310+
311+
声明的时候,在类内加`static`关键字,类外定义不能加关键字。
312+
313+
位置:直接在类后面/类对应的`cpp`,头文件中可以加`inline`
314+
315+
只有静态成员变量可以自动推断,普通的不允许。
316+
317+
用途:一个根据数量递增的ID
318+
319+
### 静态成员函数
320+
321+
静态成员函数用于访问静态全局变量。
322+
323+
有替代品:
324+
325+
- 命名空间:没有访问控制
326+
- 静态全局类对象
327+
328+
### 友元
329+
330+
在被访问的类中声明,从而使得外部的类/函数能够访问`private``protected`的对象。
331+
332+
#### 友元函数
333+
334+
在类中声明,自动成为非成员的函数,类外定义。
335+
336+
此项特性对于运算符重载非常有用。
337+
338+
#### 友元类
339+
340+
直接在类内定义。

content/posts/learncpp_6/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
+++
22
date = '2025-10-13T08:25:04+08:00'
3-
draft = true
3+
draft = false
44
title = 'cpp学习笔记(6)'
55
+++
66

content/posts/learncpp_7/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
+++
22
date = '2025-10-22T16:29:09+08:00'
3-
draft = true
3+
draft = false
44
title = 'cpp学习笔记(7)'
55
+++
66

0 commit comments

Comments
 (0)