-
Notifications
You must be signed in to change notification settings - Fork 371
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #45 from alu234/master
update: 6 articles
- Loading branch information
Showing
7 changed files
with
196 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
|
||
|
||
1. **static_cast**: `static_cast` 是用于编译时检测到的相关类型之间的转换,比如整型和浮点数、派生类和基类之间的指针或引用转换。它不能用于含有虚函数的多态基类指针或引用到派生类的转换,因为这需要运行时信息。 | ||
|
||
示例: | ||
|
||
``` | ||
int i = 10; | ||
float f = static_cast<float>(i); // 整型到浮点数的转换 | ||
``` | ||
|
||
2. **dynamic_cast**: `dynamic_cast` 主要用于处理多态性。当涉及到继承体系中的向下转换(将基类的指针或引用转换为派生类类型)时,这个转换会检查转换的安全性。如果转换无效,对于指针,它会返回nullptr;对于引用,则抛出一个`std::bad_cast`异常。使用`dynamic_cast`需要运行时类型信息(RTTI),因此它有一定的性能代价。 | ||
|
||
示例: | ||
|
||
``` | ||
Base* b = new Derived(); // 基类指针指向派生类对象 | ||
Derived* d = dynamic_cast<Derived*>(b); // 向下转型成功 | ||
if (d) { | ||
// 转型有效,'d' 不是 nullptr | ||
} | ||
``` | ||
|
||
3. **const_cast**: `const_cast` 用于移除或添加`const`或`volatile`属性。通常情况下,它被用于移除对象的常量性,允许修改原本被声明为`const`的变量。需要注意的是,去除一个本质上确实是常量的对象的`const`标记并进行修改可能会导致未定义行为。 | ||
|
||
示例: | ||
|
||
``` | ||
const int ci = 10; | ||
int& modifiable = const_cast<int&>(ci); // 移除常量性以便修改 | ||
modifiable = 20; // 注意:如果原对象真的是const,这里可能是未定义行为 | ||
``` | ||
|
||
4. **reinterpret_cast**: `reinterpret_cast` 是最危险的cast,它能够执行低级的强制类型转换。尽管几乎没有任何语义检查,但它能够在几乎任意两种类型之间转换,例如整数与指针之间的转换。由于它的不安全性,应该尽可能避免使用`reinterpret_cast`,除非你完全理解所进行的转换。 | ||
|
||
示例: | ||
|
||
``` | ||
int* p = new int(65); | ||
char* ch = reinterpret_cast<char*>(p); // 强制指针类型转换 | ||
``` | ||
|
||
总结: | ||
|
||
- `static_cast`在相关类型间做安全转换。 | ||
- `dynamic_cast`在类层次结构中转换,并支持运行时检查。 | ||
- `const_cast`改变类型的`const`或`volatile`限定。 | ||
- `reinterpret_cast`进行低级别、不安全的强制类型转换。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
在C++中,`std::atomic` 是一个模板类,用于提供对基础类型的原子操作。`std::atomic<bool>` 是该模板类针对布尔类型的特化。原子操作保证了即使在多线程环境中,每个操作也是不可分割的,从而避免了竞态条件。 | ||
|
||
`std::atomic<bool>` 底层实现通常依赖于硬件和编译器的支持来提供原子性保证。具体实现可能涉及以下几个方面: | ||
|
||
1. **内存屏障**(Memory Barriers/Fences): 内存屏障用于确保指令的执行顺序,阻止编译器或者处理器重排操作顺序。 | ||
2. **锁前缀指令**(Lock Prefix): 在x86架构中,处理器提供了带有`LOCK`前缀的指令,比如`LOCK XCHG`,它可以将操作变为原子性的。当CPU执行带有`LOCK`前缀的指令时,会确保指令完整执行,期间不会被其他处理器打断。 | ||
3. **特殊的原子指令**: 现代处理器提供了一系列原子指令,比如`XADD`(交换并加),`CMPXCHG`(比较并交换)等,可以用来实现原子变量。对于布尔变量,可能会使用这些指令来实现无锁的原子读写操作。 | ||
4. **编译器内建函数**(Compiler Intrinsics): 编译器可能提供特殊的内建函数来映射到底层的原子指令。 | ||
|
||
例如,在GCC和Clang上,通常会使用GCC的内建函数来实现原子操作。例如: | ||
|
||
``` | ||
bool old_value = __atomic_fetch_and(&my_atomic_bool, true, __ATOMIC_SEQ_CST); | ||
``` | ||
|
||
这里的 `__atomic_fetch_and` 函数是GCC提供的内建函数,用于执行原子AND操作,并返回变量的旧值。第三个参数 `__ATOMIC_SEQ_CST` 表示使用最严格的内存顺序:Sequentially Consistent。 | ||
|
||
对于不支持原子指令的数据类型或复杂操作,可能需要使用锁(比如互斥锁)来保证操作的原子性。但由于 `bool` 类型非常简单,大多数平台都能够提供无锁的原子操作支持。 | ||
|
||
不同的平台和编译器可能有不同的实现方式,因此没有一个统一的实现细节。如果你想知道具体的实现,可以查看特定编译器的源代码或者汇编输出。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
完美转发是一个与模板和函数重载相关的概念,它允许一个函数将其接收到的参数以原始的值类别(左值或右值)传递给另一个函数。这意味着**如果你传递了一个左值给包装函数,那么被调用的函数也会接收到一个左值;如果传递的是一个右值,则同样地,被调用的函数会接收到一个右值。** | ||
|
||
|
||
|
||
``` | ||
#include <utility> | ||
// 这个函数负责“转发”它的参数到另一个函数 | ||
template<typename T> | ||
void wrapper(T&& arg) { | ||
// 使用 std::forward 来确保 arg 的值类别得以保持不变 | ||
target(std::forward<T>(arg)); | ||
} | ||
// 一个可能接受左值或右值参数的目标函数 | ||
void target(int& x) { | ||
// 处理左值 | ||
} | ||
void target(int&& x) { | ||
// 处理右值 | ||
} | ||
int main() { | ||
int lv = 5; // 左值 | ||
wrapper(lv); // 应该调用 void target(int& x) | ||
wrapper(10); // 应该调用 void target(int&& x) | ||
} | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
### 左值 (Lvalue): | ||
|
||
一个**左值**表示表达式结束后依然存在的对象。它指向一个具体的内存位置,并且你可以取得其地址。通常情况下,左值表达式可能出现在赋值操作的左侧。 | ||
|
||
### 右值 (Rvalue): | ||
|
||
一个**右值**通常是暂时的并且不会长时间存在,它不能被赋予另一个值。右值通常是直接的数据值或者无法通过标识符直接访问的临时对象。 | ||
|
||
**不同点:** | ||
|
||
- **身份**: 左值具有明确的内存地址,而右值通常没有固定的内存地址。 | ||
- **持久性**: 左值代表长期存在的对象,右值代表临时或即将销毁的对象。 | ||
- **可移动性**: 右值可以被移动,而左值通常不能,除非显式地转换成右值引用。 | ||
- **引用类型**: 可以声明左值引用指向左值(`T&`),而右值引用(`T&&`)可以绑定到右值上,优化资源使用。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
内存池是一种内存分配方式,它预先在内存中分配一定数量的块或对象,形成一个“池”。当程序需要分配内存时,它从这个池中分配一个块;当内存被释放时,这个块返回到池中以供再次使用。内存池可以显著减少频繁分配和释放内存所带来的开销,并且有助于避免内存碎片化,提高内存使用效率。 | ||
|
||
下面展示了如何设计一个简单的内存池,这个简单的内存池设计包括以下几个关键特性: | ||
|
||
- **预分配**:在构造函数中预先分配了一定数量的固定大小内存块。 | ||
- **分配与释放**:`allocate` 方法从池中分配一个内存块,而 `deallocate` 方法则将不再使用的内存块返还给池。 | ||
- **管理策略**:本例中使用 `std::list` 来管理空闲内存块,但实际应用中可能需考虑更高效的数据结构。 | ||
|
||
``` | ||
#include <iostream> | ||
#include <list> | ||
class MemoryPool { | ||
public: | ||
MemoryPool(size_t size, unsigned int count) { | ||
for (unsigned int i = 0; i < count; ++i) { | ||
freeBlocks.push_back(new char[size]); | ||
} | ||
blockSize = size; | ||
} | ||
~MemoryPool() { | ||
for (auto block : freeBlocks) { | ||
delete[] block; | ||
} | ||
} | ||
void* allocate() { | ||
if (freeBlocks.empty()) { | ||
throw std::bad_alloc(); | ||
} | ||
char* block = freeBlocks.front(); | ||
freeBlocks.pop_front(); | ||
return block; | ||
} | ||
void deallocate(void* block) { | ||
freeBlocks.push_back(static_cast<char*>(block)); | ||
} | ||
private: | ||
std::list<char*> freeBlocks; | ||
size_t blockSize; | ||
}; | ||
// 使用示例 | ||
int main() { | ||
const int blockSize = 32; // 块大小 | ||
const int blockCount = 10; // 块数量 | ||
MemoryPool pool(blockSize, blockCount); | ||
// 分配内存 | ||
void* ptr1 = pool.allocate(); | ||
void* ptr2 = pool.allocate(); | ||
// 使用ptr1和ptr2... | ||
// 释放内存 | ||
pool.deallocate(ptr1); | ||
pool.deallocate(ptr2); | ||
return 0; | ||
} | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
在C++11及之后的标准中,为了给开发者提供更细粒度的控制以及可能的性能优化空间,引入了多种内存顺序选项: | ||
|
||
1. **memory_order_relaxed**:放松的内存顺序。不对执行顺序做任何保证,除了原子操作本身的原子性。这意味着,在没有其他同步操作手段的情况下,读写操作的顺序可能与程序代码中的顺序不一致。 | ||
2. **memory_order_consume**:较轻量级的保序需求,用于指定操作依赖于先前的某些操作结果。这在实际实现中通常被视为与`memory_order_acquire`相同。 | ||
3. **memory_order_acquire**:获取操作,禁止后续的读或写被重排到当前操作之前。用于读取操作。 | ||
4. **memory_order_release**:释放操作,防止之前的读或写操作被重排到当前操作之后。用于写入操作。 | ||
5. **memory_order_acq_rel**:同时包含获取和释放语义。适用于同时具有读取和写入特性的操作。 | ||
6. **memory_order_seq_cst**:顺序一致性内存顺序。所有线程看到的操作顺序一致。这是默认的内存顺序,并且提供了最强的顺序保证。 | ||
|
||
内存顺序的选择影响着程序的正确性和性能。较弱的内存顺序(例如`memory_order_relaxed`)可能带来更好的性能,因为它们允许更多的指令重排序;但是,使用它们也需要更小心地设计程序,以避免数据竞争和其他并发相关的错误。相反,`memory_order_seq_cst`提供了最简单和最直观的并发模型,但可能因为额外的同步代价而影响性能。 |