first commit

This commit is contained in:
taynpg 2024-03-08 15:25:16 +08:00
commit ca66456a83
34 changed files with 1806 additions and 0 deletions

32
.clang-format Normal file
View File

@ -0,0 +1,32 @@
# .clang-format
# 风格格式化
BasedOnStyle: LLVM
# 4 空格缩进
IndentWidth: 4
# 连续对齐变量的声明
AlignConsecutiveDeclarations: true
# 指针左侧对齐
PointerAlignment: Left
# 访问说明符(public、private等)的偏移
AccessModifierOffset: -4
# 大括号
BreakBeforeBraces: Custom
BraceWrapping:
# 函数定义后面大括号在新行
AfterFunction: true
# class定义后面
AfterClass: true
# 去除C++11的列表初始化的大括号{后和}前的空格
Cpp11BracedListStyle: true
# 允许重新排版注释
ReflowComments: true
# 允许排序#include
SortIncludes: true
# 在尾随的评论前添加的空格数(只适用于//)
SpacesBeforeTrailingComments: 3
# tab宽度
TabWidth: 4
# 构造函数的初始化列表要么都在同一行,要么都各自一行
ConstructorInitializerAllOnOneLineOrOnePerLine: true

13
.clangd Normal file
View File

@ -0,0 +1,13 @@
Hover:
ShowAKA: Yes
Diagnostics:
UnusedIncludes: None # 禁用未使用头文件提示
Suppress: [
anon_type_definition, # 禁用匿名的typedef提示
unused-variable, # 禁用未使用变量提示
unused-function, # 禁用未使用函数提示
unused-includes, # 禁用未使用的头文件提示
unused-private-field, # 禁用未使用的私有成员提示
]
ClangTidy:
Remove: misc-unused-alias-decls

42
.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
build
.cache
third/glfw-3.3.8.bin.WIN64
.vs
outBuild
cout
.DS_Store
.idea
out
cmake-*

37
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,37 @@
{
"files.autoSave": "onFocusChange",
"editor.fontSize": 14,
"editor.fontFamily": "'FiraCode Nerd Font Mono', 'FiraCode Nerd Font Mono', 'FiraCode Nerd Font Mono'",
"cmake.configureOnOpen": true,
"cmake.debugConfig": {
"console": "integratedTerminal",
},
"cmake.generator": "Ninja",
"cmake.options.statusBarVisibility": "visible",
"C_Cpp.intelliSenseEngine": "disabled",
"clangd.arguments": [
"--header-insertion=never",
"--all-scopes-completion",
"--completion-style=detailed",
"--clang-tidy",
"-j=4",
"--pch-storage=memory",
"--compile-commands-dir=build",
"--background-index",
"--ranking-model=heuristics",
"--function-arg-placeholders=false",
"--query-driver=/usr/bin/clang++"
],
"editor.inlayHints.enabled": "off",
"editor.unicodeHighlight.allowedLocales": {
"ja": true,
"zh-hant": true,
"zh-hans": true
},
"files.associations": {
"iostream": "cpp"
},
"workbench.colorCustomizations": {
//"editor.background": "#C0C0C0"
}
}

11
CMakeLists.txt Normal file
View File

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.8)
project(compre)
set(CMAKE_CXX_STANDARD 11)
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
string(APPEND CMAKE_CXX_FLAGS " /source-charset:utf-8")
endif()
add_subdirectory(cppbase)
add_executable(compre main.cpp)

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# 说明
该仓库用于记录:cpp的语法、语言、或系统、或工具、的一些基本用法。

41
cppbase/.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
build
.cache
third/glfw-3.3.8.bin.WIN64
.vs
outBuild
cout
.DS_Store
.idea
out

12
cppbase/CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.8)
project(cppbase)
set(CMAKE_CXX_STANDARD 11)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/structure)
add_subdirectory(STL)
add_executable(cppbase
"main.cpp" "grammar/grammar.cpp"
"grammar/grammar.h" "algorithm/CAlgorithm.cpp" "algorithm/CAlgorithm.h"
)
add_executable(algorithm_bin "algrorithm.cpp" "algorithm/ABasic.cpp" "algorithm/ABasic.h")
add_executable(object_bin "object.cpp")

View File

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.8)
project(cppstl)
set(CMAKE_CXX_STANDARD 11)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(cppstl main.cpp
"STLUse.h" "STLUse.cpp"
"smart_ptr.h" "smart_ptr.cpp"
)

254
cppbase/STL/STLUse.cpp Normal file
View File

@ -0,0 +1,254 @@
#include "STLUse.h"
#include <algorithm>
#include <functional>
#include <iostream>
#include <stack>
#include <string>
#include <vector>
void show_demo()
{
stack_demo();
adapter_demo();
search_demo();
}
// 概述
void summarize()
{
// 1.STL: 容器、算法、迭代器、仿函数、适配器、空间配置器。
// 2.容器
// (1)顺序容器: vector[动态数组]、deque[双向队列]、list[双向链表]
// (2)关联容器:set/multiset[集合]、map/multimap[字典]
// (3)容器适配器<从deque封装>:stack[栈]、queue[队列]、priority_queue[优先级队列]
// 3.各容器简介
// (1)vector: 内存连续。
// (2)deque: 内存连续,在两端增删具有较佳的性能。
// (3)list: 内存不连续,不随机存取。
// (4)集合带 multi 的,允许 key 重复。
// 4.map hashtable deque list实现原理
// (1)map: 内部是红黑树实现,可以自动排序,因此内部元素是有序的。
// (2)hashtable: 函数映射的思想记录存储位置和关键字。
// (3)deque: 内部实现是双向队列。
// (4)list: 内部实现是双向链表。
// 5.容器操作复杂度
// (1)vector,插入[O(N)]、查看[O(1)]、删除[O(N)]。
// (2)deque,插入[O(N)]、查看[O(1)]、删除[O(N)]。
// (3)list,插入[O(1)]、查看[O(N)]、删除[O(1)]。
// (4)map、set、multiset、multimap<红黑树,平衡二叉树>,插入[O(logN)]、查看[O(logN)]、删除[O(logN)]。
// (5)unordered_map、unordered_set、unordered_multimap、
// unordered_multiset:
// 插入:O(1),最坏O(N)
// 查看:O(1),最坏O(N)
// 删除:O(1),最坏O(N)
// x.空间适配器
}
void stack_demo()
{
std::stack<SimuData> stack{};
// 不提供遍历行为
SimuData da{};
da.data_ = 89;
da.value_ = 5.2;
stack.push(da);
while (!stack.empty()) {
std::cout << stack.top().data_ << std::endl;
stack.pop(); // 出栈
}
}
void queue_demo() // 队列
{
// 无迭代器
}
void list_demo()
{
// 双向循环链表
// 性质:插入删除操作不会造成原有的迭代器实现,vector 不行。
}
void set_demo()
{
// 特性:key 会被自动排序,不允许有重复的值。
// set 的 iterator 是一种 const_iterator,因为 set的元素值就是
// 键值,关系到 set 元素的排序规则。
}
void multi_set_demo()
{
// 与 set 特性用法一致。差别在于可以key重复。
// 与 set 一样底层实现使用的是红黑树。
}
// binary_function 指二元 (这个函数被标记了 弃用)
// class A : public std::binary_function<int, int, void>
// {
// public:
// void operator()(int val, int start) const
// {
// std::cout << "val:" << val << " start:" << start
// << " total:" << val + start << "\n";
// }
// };
// 取反适配器 (unary_function 这个函数被标记了 弃用)
// class MGreator : public std::unary_function<int, bool>
// {
// public:
// bool operator()(int val) const
// {
// // 大于 3
// return val > 3;
// }
// };
void my_print(int val) { std::cout << val << std::endl; }
void my_print2(int val, int start)
{
std::cout << val << " " << start << std::endl;
}
class Person
{
public:
Person(std::string name, int age)
{
name_ = name;
age_ = age;
}
void print()
{
std::cout << age_ << " ";
std::cout << name_ << "\n";
}
std::string name_{};
int age_{};
};
void my_print3(const Person& person)
{
std::cout << person.age_ << " ";
std::cout << person.name_ << "\n";
}
// 仿函数的目的:协助算法完成不同的策略。
void adapter_demo()
{
std::vector<int> vec{};
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
// bind2nd 将45绑定到第二个参数, bind1st 将45绑定到第一个参数
// 《函数对象适配器》 (bind2nd 这个函数被标记了 弃用)
// std::for_each(vec.begin(), vec.end(), std::bind2nd(A(), 45));
// std::vector<int>::iterator iter =
// std::find_if(vec.begin(), vec.end(), MGreator());
// if (iter != vec.end()) {
// // 找到 *iter
// std::cout << *iter << std::endl;
// }
// (not1 ==> 一元的取反)
// iter = std::find_if(vec.begin(), vec.end(), std::not1(MGreator()));
// if (iter != vec.end()) {
// // 找到 *iter
// std::cout << *iter << std::endl;
// }
// 这是另一个示例 deprecated-declarations (bind2nd 这个函数被标记了 弃用)
// iter = std::find_if(vec.begin(), vec.end(),
// std::not1(std::bind2nd(std::greater<int>(), 3)));
// if (iter != vec.end()) {
// // 找到 *iter
// std::cout << *iter << std::endl;
// }
// 《函数适配器》
std::for_each(vec.begin(), vec.end(), my_print);
// 将函数指针适配成函数的对象 ptr_fun (bind2nd ptr_fun 这个函数被标记了 弃用)
// std::for_each(vec.begin(), vec.end(),
// std::bind2nd(std::ptr_fun(my_print2), 1000));
// 《成员函数适配器》
std::vector<Person> pvec{};
pvec.emplace_back("A", 1);
pvec.emplace_back("B", 2);
pvec.emplace_back("C", 3);
pvec.emplace_back("D", 4);
pvec.emplace_back("E", 5);
std::for_each(pvec.begin(), pvec.end(), my_print3);
// 利用 mem_fun_ref 适配一下。 (mem_fun_ref 这个函数被标记了 弃用)
// std::for_each(pvec.begin(), pvec.end(), std::mem_fun_ref(&Person::print));
}
// 常用查找算法
void search_demo()
{
std::vector<int> demo{};
demo.push_back(23);
demo.push_back(21);
demo.push_back(87);
demo.push_back(11);
demo.push_back(11);
demo.push_back(6);
demo.push_back(90);
// find 直接按照值去查找
std::vector<int>::iterator iter = std::find(demo.begin(), demo.end(), 11);
if (iter != demo.end()) {
std::cout << *iter << std::endl;
}
// find_if 使用你提供的条件去查找
// 同理 count 和 count_if 也是如此。
// adjacent_find 查找相邻的重复元素
iter = std::adjacent_find(demo.begin(), demo.end());
if (iter != demo.end()) {
std::cout << "相邻的重复元素:" << *iter << std::endl;
}
// binary_search 二分查找法,需要有序。
}
void predicate_demo()
{
// 谓词是指普通函数或重载的operator(返回值是bool类型的函数对象(仿函数)
}
void traverse_demo()
{
// 遍历算法
// transform 用于搬运
}
// 排序
void sort_demo()
{
// merge 合并,两个容器需要有序
// sort 排序
// random_shuffle 随机调整次序
// reverse 翻转
}
// 集合算法
void set_algorithm_demo()
{
// set_intersection 求交集
// set_unin 求并集
// set_difference 差集
}

34
cppbase/STL/STLUse.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
struct SimuData {
int data_{};
double value_{};
};
// 概述
void summarize();
void show_demo();
void stack_demo();
void queue_demo();
void list_demo();
void set_demo();
void multi_set_demo();
void adapter_demo();
void search_demo();
// 谓词示例
void predicate_demo();
void traverse_demo();
void sort_demo();
void set_algorithm_demo();

7
cppbase/STL/main.cpp Normal file
View File

@ -0,0 +1,7 @@
#include <iostream>
#include "STLUse.h"
int main() {
show_demo();
return 0;
}

24
cppbase/STL/smart_ptr.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "smart_ptr.h"
// 基本
void base()
{
// 1. shared_ptr 智能指针用于智能管理内存管理动态分配的内存,可以确保在不再需要使用某段内存时进行自动释放,避免内存泄漏。
// 通过跟踪引用计数或者其他机制,智能指针可以在不再有任何引用指向分配的内存时释放它。
// 2. weak_ptr 用于解决 shared_ptr 的循环引用的问题。
// (1) weak_ptr 只能从 shared_ptr 对象构建。
// (2) weak_ptr 并不影响动态对象的生命周期,即其存在与否并不影响对象的引用计数器。当 weak_ptr 所指向的对象因为 shared_ptr 计数器为 0 而被释放后,
// 那么 weak_ptr 的 lock 方法将返回空。
// (3) weak_ptr 并没有重载 operator-> 和 operator * 操作符,因此不可直接通过 weak_ptr 使用对象。
// (4) 提供了 expired() 与 lock() 成员函数,前者用于判断 weak_ptr 指向的对象是否已被销毁,后者返回其所指对象的 shared_ptr 智能指针(对象销毁时返回”空“ shared_ptr ),
// 如果返回 shared_ptr,那么计数器会加1.
}
// 测试用例
void test_example()
{
MSmartPointer<CHouse> p1(new CHouse());
MSmartPointer<CHouse> p2;
p2 = p1;
}

67
cppbase/STL/smart_ptr.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include "../ex_object.h"
// 基本
void base();
// 自定义一个简单的智能指针
template <typename T>
class MSmartPointer {
private:
T* ptr;
size_t* refCount;
public:
MSmartPointer() : ptr(nullptr), refCount(nullptr) {}
explicit MSmartPointer(T* p) : ptr(p), refCount(new size_t(1)) {}
// 拷贝构造函数
MSmartPointer(const MSmartPointer& other)
: ptr(other.ptr), refCount(other.refCount)
{
if (!refCount) {
return;
}
++(*refCount);
}
// 析构函数
~MSmartPointer()
{
if (refCount && --(*refCount) <= 0) {
delete ptr;
delete refCount;
ptr = nullptr;
refCount = nullptr;
}
}
// 重载赋值操作符
MSmartPointer& operator=(const MSmartPointer& other)
{
if (this == &other) {
return *this;
}
if (refCount && --(*refCount) <= 0) {
delete ptr;
delete refCount;
}
ptr = other.ptr;
refCount = other.refCount;
if (refCount) {
++(*refCount);
}
return *this;
}
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
};
// 测试用例
void test_example();

View File

@ -0,0 +1,94 @@
#include "ABasic.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <iterator>
#include <vector>
/*
next_permutation
[first, last) [first, last)
next_permutation
使
true false
cppreference
[first, last) operator< comp
true
std::sort(first, last) false
123 132
next_permutation
next_permutation
false
*/
// 生成 N 个不同元素的全排列
void generatePermutations()
{
int elements[] = {1, 2, 3, 4};
const size_t N = sizeof(elements) / sizeof(elements[0]);
std::vector<int> vec(elements, elements + N);
int count = 0;
do {
std::cout << ++count << ": ";
// 打印写法
std::copy(vec.begin(), vec.end(),
std::ostream_iterator<int>(std::cout, ", "));
std::cout << std::endl;
} while (next_permutation(vec.begin(), vec.end()));
}
/*
7 3
{ 1, 1, 1, 0, 0, 0, 0 }
1
*/
void combinationDemo()
{
int values[] = {1, 2, 3, 4, 5, 6, 7};
int elements[] = {1, 1, 1, 0, 0, 0, 0};
const size_t N = sizeof(elements) / sizeof(elements[0]);
assert(N == sizeof(values) / sizeof(values[0]));
std::vector<int> selectors(elements, elements + N);
int count = 0;
do {
std::cout << ++count << ": ";
for (size_t i = 0; i < selectors.size(); ++i) {
if (selectors[i]) {
std::cout << values[i] << ", ";
}
}
std::cout << std::endl;
} while (prev_permutation(selectors.begin(), selectors.end()));
}
// 这里写一个仿函数 或者 使用 lambda 表达式都可以
struct AreBothSpaces {
bool operator()(char x, char y) const { return x == ' ' && y == ' '; }
};
// 移除连续空格
void removeContinuousSpaces(std::string& str)
{
std::cout << "PreString:" << str << std::endl;
// std::string::iterator last =
// std::unique(str.begin(), str.end(), AreBothSpaces());
std::string::iterator last2 =
std::unique(str.begin(), str.end(),
[](char x, char y) { return x == ' ' && y == ' '; });
str.erase(last2, str.end());
std::cout << "AfterString:" << str << std::endl;
}

View File

@ -0,0 +1,11 @@
#pragma once
#include <string>
// 生成 N 个不同元素的全排列
void generatePermutations();
// 组合
void combinationDemo();
// 移除连续空格
void removeContinuousSpaces(std::string& str);

View File

@ -0,0 +1,139 @@
//
// Created by TYP on 2023/9/2.
//
#include "CAlgorithm.h"
void CAlgorithm::SortDirectSelectMethod(int array[], int nSize)
{
if (nSize < 2)
return;
int index = -1;
int nTemp = -1;
for (int i = 0; i < nSize - 1; ++i) {
// 进行 n-1 趟选择
index = i + 1;
// 从无序区选取最小的记录
for (int j = index; j < nSize; ++j) {
if (array[index] > array[j])
index = j;
}
nTemp = array[i];
array[i] = array[index];
array[index] = nTemp;
// printf("Step %d:\n", i);
// PrintArrayInt(array, nCnt);
}
}
void CAlgorithm::SortQuickMethod(int array[], int nStartIndex, int nEndIndex)
{ /*NOLINT*/
// 基准值和一个临时变量
int nBasic = array[nStartIndex];
int nTemp = -1;
// 保存起始位置
int nStart = nStartIndex;
int nEnd = nEndIndex;
while (nStart < nEnd) {
// 从右向左搜索小于基准值的数, 比基准值大的(不包含相等)则向左边挪动
while (nStart < nEnd && nBasic < array[nEnd])
--nEnd;
// 从左向右搜索大于基准值的数, 比基准值小的(不包含相等)则向右边挪动
while (nStart < nEnd && nBasic > array[nStart])
++nStart;
// start 在 end 的右侧则查询无效。
if (nStart >= nEnd)
continue;
// 交换数据
nTemp = array[nEnd];
array[nEnd] = array[nStart];
array[nStart] = nTemp;
// printf("分块前 while 循环内:");
// PrintArrayInt(array, 8);
}
// 跳出循环之后,把更换基准值
nTemp = nBasic;
nBasic = array[nStart];
array[nStart] = nTemp;
// start 和 end
// 碰头之后就把数据分成了左右两块,分别对左右两个块作相同的处理。
if (nStartIndex < nEnd)
SortQuickMethod(array, nStartIndex, nEnd - 1);
if (nStart < nEndIndex)
SortQuickMethod(array, nEnd + 1, nEndIndex);
}
void CAlgorithm::SortBubbleMethord(int array[], int nSize)
{
if (nSize < 2)
return;
int nTemp = -1;
// 不需要和自己比较,比较次数 -1
for (int i = 0; i < nSize - 1; ++i) {
int count = 0;
for (int j = 0; j < nSize - 1 - i; ++j) {
// 升序
if (array[j] > array[j + 1]) {
nTemp = array[j];
array[j] = array[j + 1];
array[j + 1] = nTemp;
count = 1;
}
}
// 如果某一趟没有交换位置,则说明已经排好序,直接退出循环
if (count == 0)
break;
}
}
void SortDirectSelectMethodTest()
{
int data[] = {34, 546, 12, 67, 77, 126, 980, 346};
int nSize = sizeof(data) / sizeof(int);
printf("Sort Before:");
PrintArrayInt(data, nSize);
printf("data's size:%d\n", nSize);
CAlgorithm::SortDirectSelectMethod(data, nSize);
printf("Sort After:");
PrintArrayInt(data, nSize);
}
void SortQuickMethodTest()
{
int data[] = {34, 546, 12, 67, 77, 126, 980, 346};
int nSize = sizeof(data) / sizeof(int);
printf("Sort Before:");
PrintArrayInt(data, nSize);
printf("data's size:%d\n", nSize);
CAlgorithm::SortQuickMethod(data, 0, 7);
printf("Sort After:");
PrintArrayInt(data, nSize);
}
void SortBubbleMethordTest()
{
int data[] = {34, 546, 12, 67, 77, 126, 980, 346};
int nSize = sizeof(data) / sizeof(int);
printf("Sort Before:");
PrintArrayInt(data, nSize);
printf("data's size:%d\n", nSize);
CAlgorithm::SortBubbleMethord(data, 8);
printf("Sort After:");
PrintArrayInt(data, nSize);
}

View File

@ -0,0 +1,118 @@
//
// Created by TYP on 2023/9/2.
//
#ifndef COMPRE_CALGORITHM_H
#define COMPRE_CALGORITHM_H
#include <cstdio>
void PrintArrayInt(const int array[], int nCnt)
{
if (nCnt < 1)
return ;
for (int i = 0; i < nCnt; ++i)
std::printf("%d ", array[i]);
printf("\n");
}
class CAlgorithm {
public:
CAlgorithm() = default;
~CAlgorithm() = default;
public:
/*
* 1.
*
* ``
* O(n^2)
* O(1)
* i a[i]~a[n]
* i i
*
*
* https://blog.csdn.net/u011815404/article/details/79256237
*/
static void SortDirectSelectMethod(int array[], int nSize);
/*
* 2.
*
*
* O(n^2),O(nlogn);O(nlogn);
* O(logn)
*
*
*
*
*
* https://blog.csdn.net/weixin_61453872/article/details/121481223
*/
static void SortQuickMethod(int array[], int nStartIndex, int nEndIndex);
/*
* 3.
*
*
* O(n^2)
* O(1)
*
*
*/
static void SortBubbleMethord(int array[], int nSize);
/*
* 4.
*
*
*
* O(nlogn)
* o(n)
* img/.png
*/
/*
* 5.
*
*
* O(n^2)
* O(1)
*/
/*
* 6.
*
*
* O(n^(1.32))
* O(1)
* img/.jpeg
*/
/*
* 7.
*
*
* O(nlogn)
* O(1)
*/
/*
* 8.
*
*
* O(n^2)
* O(1)
* ...
*/
};
// 直接排序测试
void SortDirectSelectMethodTest();
// 快速排序测试
void SortQuickMethodTest();
// 冒泡排序测试
void SortBubbleMethordTest();
#endif //COMPRE_CALGORITHM_H

10
cppbase/algrorithm.cpp Normal file
View File

@ -0,0 +1,10 @@
#include <iostream>
#include "algorithm/ABasic.h"
int main()
{
std::string data("Nihao Liming Zai na li?");
removeContinuousSpaces(data);
return 0;
}

47
cppbase/ex_object.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "ex_object.h"
// 左值引用与右值引用
void lrvalue()
{
// 左值引用与右值引用有什么区别,左值引用和右值引用的目的是什么?
}
// 复制构造函数,仅接受左值。
// CHouse::CHouse(CHouse& rh)
// {
// std::cout << "构造函数(仅接受左值)" << std::endl;
// }
// 复制构造函数,左值右值均可。
CHouse::CHouse(const CHouse& rh)
{
std::cout << "构造函数(左值右值均可)" << std::endl;
}
// 移动构造函数仅接受右值。
CHouse::CHouse(CHouse&& rv) noexcept
{
// 移动语义要修改rv的值,故不能使用const。
std::cout << "移动构造函数(仅接受右值)" << std::endl;
}
// 赋值构造函数,左右值均可。
CHouse& CHouse::operator=(const CHouse& rh)
{
// 需要前面已经定义,否则调用的时复制构造函数。ß
std::cout << "赋值构造函数(左右值均可)" << std::endl;
return *this;
}
// 赋值构造函数,仅接受右值。
// CHouse& CHouse::operator=(CHouse&& rv) noexcept
// {
// std::cout << "赋值构造函数(仅接受右值)" << std::endl;
// return *this;
// }
void demo()
{
CHouse houseA;
CHouse houseB = std::move(houseA);
CHouse houseC = houseB;
houseA = houseB;
CHouse houseD(houseC);
CHouse houseE(std::move(houseD));
}

23
cppbase/ex_object.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <iostream>
// 左值引用与右值引用
void lrvalue();
// std::move 与 类的各种构造
class CHouse
{
public:
CHouse() = default;
~CHouse() = default;
public:
//CHouse(CHouse& rh); // 复制构造函数,仅接受左值。
CHouse(const CHouse& rh); // 复制构造函数,左值右值均可。
CHouse(CHouse&& rv) noexcept; // 移动构造函数仅接受右值。
CHouse& operator=(const CHouse& rh); // 赋值构造函数,左右值均可。
//CHouse& operator=(CHouse&& rv) noexcept; // 赋值构造函数,仅接受右值。
};
void demo();

390
cppbase/grammar/grammar.cpp Normal file
View File

@ -0,0 +1,390 @@
#include "grammar.h"
#include <iostream>
#include <vector>
// key: static 修饰全局变量
// 本来全局变量可以在同工程其他源文件访问(添加 extern),
// 添加之后仅本文件可见。
static double ConstAndStatic_sDouble = 5.77;
class CConstAndStatic_DemoA
{
public:
CConstAndStatic_DemoA() = default;
~CConstAndStatic_DemoA() = default;
public:
// Run属于类而不属于某个特定的对象
static void Run() {}
private:
// 初始化只能在类外初始化,这里仅是声明。
static int m_Code;
};
// 该变量先于对象存在
int CConstAndStatic_DemoA::m_Code = 99;
// key:修饰函数和修饰全局变量类似
static void ConstAndStatic_Function()
{
// 静态变量什么时候初始化?
// C:任何代码执行之前。
// CPP:全局或静态对象首次使用才会构造。
}
// const和static关键字
void ConstAndStatic()
{
// key: static 修饰局部变量
// static 修饰的数据会放在:静态数据区,生命周期到程序结束
// static 静态函数只能在本源文件中使用。
// 但是作用域并没有改变
static int s_Int = 56;
// K: static函数和变量受 [模块] 范围管制。
// ===const 基本类型,类型符左右都行
const int nData = 32;
//
int nCn = 55;
const int* pData = &nCn; // 值不能改变
int* const pData1 = &nCn; // 指针不能改变
// ===const 在类中的用法:
// 在对象内是常量,对类而言是可变的,因为要创建对象,所以不能在类声明中初始化。
// const 和 static 在类中是不能同时使用的,因为 const 需要具体到某个对象。
}
void basic()
{
// x.sturct 和 class 的区别
// 1.权限(包括成员权限、继承权限)
// 2.数据结构封装(struct),对象的封装(class)
// 3.class 可以用于定义模板参数,struct 不可以。
// class
// 若没有用到解引用的地方:A* a = nullptr, a->print(); 是正确的。
// new 和 malloc 区别
// x.new 是操作符,malloc 是函数。
// x.构造
// x.new 可以重载。
// x.返回值。
// 内联函数
// x.避免了函数调用开销。
// x.不允许循环语句开关语句,有就无意义。
// 传递方式:值、引用、指针。
// 重写、重载
// 重载:函数名相同,参数列表不同。使用 name mangling
// (倾轧),改变函数名,编译阶段。
// 四类构造函数:默认、初始化、拷贝、移动。
//
}
void to_binary_progress()
{
// 1.预编译、编译、汇编、链接。
// 预编译:去除define,展开宏定义,处理条件预编译,注释行号等。
// 编译:词法分析、语法分析、语义分析,代码优化和目标代码生成。
// 汇编:将汇编代码编译成机器指令。
// 链接:将目标文件链接形成可执行程序。
}
// C和C++的区别
void DiffBetweenCppAndC()
{
// 1.基本语法没有什么大的区别
// 2.C++有新增的语法和关键字,如命名空间、内存管理方式不同、auto、nullptr、引用等
// x.封装、继承、多态。
// x.四类 cast 转换。
// 3.C++有重载和虚函数概念
// 4.C++的struct可以成员函数,且有访问权限的概念。
// 5.C++有模板用于重用代码
}
void memory_use()
{
// 分配方式:
// 1.栈
// 2.堆,new 分配的。
// 3.自由存储区,malloc 分配。
// 4.全局/静态存储区,同一块内存。
// 5.常量存储区,不允许修改。
}
// C++的四个类型转换
void FourTypeConvert()
{
// 1.static_cast:
// 没有动态类型检查,上行转换(派生类->基类)安全,下⾏转换(基类->派生类)
// 不安全,所以主要执行多态的转换操作
// 2.dynamic_cast:专门于派生类之间的转换,type-id 必须是类指针,类引用或
// void*,对于下行转换是安全的,当类型不一致时,转换过来的是空指针
// 3.const_cast: 专门于 const 属性的转换,去除 const 性质,或增加 const
// 性质, 是四个转换符中唯一个可以操作常量的转换符。
// 4.reinterpret_cast:没有任何类型检查和格式转换,仅仅是简单的二进制数据拷贝。
}
// 指针和引用的区别
void PointerAndReference()
{
// 1. 都是一种内存地址的概念,指针是一个实体,引用只是一个别名
// 2.
// 引用必须且只能在定义时被绑定到一块内存上,后续不能更改,也不能为空,也没有
// const 和非 const 区别
// 3. 指针包含的内容是可以改变的,允许拷贝和赋值,有 const 和非 const
// 区别,甚⾄可以为空,sizeof 指针得到的是指针类型的大小。
}
// volatile 和 extern 关键字
void VolaAndExtern()
{
// ===<1> volatile
// 1.易变性:下一条语句不会直接使用上一条语句对应的 volatile
// 变量的寄存器内容,而是重新从内存中读取
// 2.不可优化性:不要对我这个变量进行各种激进的优化
// 3.能够保证 volatile 变量之间的顺序性,编译器不会进行乱序优化。
// ===<2> extern ⽤来说明 “此变量/函数是在别处定义的,要在此处引用
// 1.extern 会加速程序的编译过程,这样能节省时间
// 2.哪里声明才能在哪里使用
// 3.在 C++ 中 extern 还有另外一种作用,用于指示 C 或者
// C++函数的调用规范。
}
// define 和 const
void DefineAndConst()
{
// 1.define 以立即数的方式保留了多份数据的拷贝
// 2.const 是在编译期间进行处理的,const
// 有类型,也有类型检查,程序运行时系统会为 const 常量
// 分配内存,而且从汇编的角度讲,const
// 常量在出现的地方保留的是真正数据的内存地址,只保留了一份数据的
// 拷贝,省去了不必要的内存空间。而且,有时编译器不会为普通的 const
// 常量分配内存,而是直接将 const 常量添
// 加到符号表中,省去了读取和写入内存的操作,效率更高。
}
class CalcClassSize_A
{
};
class CalcClassSize_B
{
public:
virtual void fun() {}
};
class CalcClassSize_C
{
public:
static int m_nA;
};
class CalcClassSize_D
{
public:
int m_nA;
};
// 计算类的大小
void CalcClassSize()
{
// 空类在实例化时得到一个独一无二的地址,所以为 1
std::cout << "CalcClassSize_A Size: " << sizeof(CalcClassSize_A) << "\n";
// 当 C++ 类中有虚函数的时候,会有一个指向虚函数表的指针(vptr)
std::cout << "CalcClassSize_B Size: " << sizeof(CalcClassSize_B) << "\n";
//
std::cout << "CalcClassSize_C Size: " << sizeof(CalcClassSize_C) << "\n";
//
std::cout << "CalcClassSize_D Size: " << sizeof(CalcClassSize_D) << "\n";
}
void class_relate()
{
// 构造函数为什么不能是虚函数
// 1.虚函数只需要知道函数接口而不需要知道对象的具体类型,但是如果使用构造创建对象的话,则必须知道对象
// 的完整信息,特别是具体类型,如果说构造函数是虚的话,那么虚函数表指针则不存在,也是违反了先实例化
// 后调用的规则(虚表在实例化后才存在)。
// 虚函数表是一个存储虚函数地址的数组,以NULL结尾,在编译阶段生成,位于程序数据段,内存开辟后,写入对象的vfptr,再调用构造函数。
}
// 面向对象的三大特性、封装,继承、多态
void EncapInheriAndPolyMorphism()
{
// 封装:一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体,将客观事物封装成抽象的类
// 继承:可以让某个类型的对象获得另一个类型的对象的属性的方法。
// 即一个接口,可以实现多种方法。
// 多态:同一个行为具有多个不同表现形式或形态的能力,通常通过继承和接口实现。
// Note: 多态的访问权限以基类为主。
}
// lambda 表达式
void Lambda()
{
// 1.相当于匿名函数
// 2.lamdba 表达式产生的是函数对象。
// 3.在类中,可以重载函数调用运算符(),此时类的对象可以将具有
// 类似函数的行为,我们称这些对象为函数对象(Function
// Object)或者仿函数(Functor)。
// 适配器:将某些已经存在的东西进行限制或者组合变成一个新的东西,这个新的东西体现一些新的特性,但底层都是由一些已经存在的东西实现的。
auto add = []() -> int { return 33; };
// 最前边的 [] 是 lambda 表达式的一个很重要的功能,就是 闭包。
// 大致原理:每当你定义一个 lambda
// 表达式后,编译器会自动生成一个匿名类(这个类当然重载了()运算符)
// 我们称为闭包类型(closure type)。那么在运行时,这个 lambda
// 表达式就会返回一个匿名的闭包实例,其实是一个右值。
// 闭包的一个强大之处是其可以通过传值或者引用的方式捕捉其封装作用域内的变量,前面的方括号就是用来定义捕
// 捉模式以及变量,我们⼜将其称为 lambda 捕捉块。
// lambda
// 表达式一个更加要的应用是其可以用于函数的参数,通过这种方式可以实现回调函数。
}
// auto 和 decltype
void AutoAndDecltype()
{
// C++ 提供了 auto 和 decltype 来静态推导类型
// Decltype: 用于获取一个表达式的类型,而不对表达式进行求值(类似于 sizeof
// )。 decltyp(e) 规则如下 若 e
// 为一个无括号的变量、函数参数、类成员,则返回类型为该变量 / 参数 /
// 类成员在源程序中的声明类型;
// 否则的话,根据表达式的值分类(value categories),设设 T 为 e 的类型:
// 的类型:
// 若 e 是一个左值(lvalue,即“可寻址值”),返回 T& ;
// 若 e 是一个临终值(xvalue),则返回值为 T&& ;
// 若 e 是一个纯右值(prvalue),则返回值为 T 。
const std::vector<int> v(1);
const int&& foo(); // 返回临终值:生命周期已结束但内存还未拿走
auto a = v[0]; // a 为 int
decltype(v[0]) b =
0; // b 为 const int&
// 即 vector<int>::operator[](size_type) const 的返回值类型
auto c = 0; // c, d 均为 int
auto d = c;
decltype(c) e; // e 为 int,即 c 的类型
decltype((c)) f = e; // f 为 int&,因为 c 是左值
decltype(0) g{}; // g 为 int,因为 0 是右值
}
// 静态 assert
void StaticAssert()
{
// C++ 提供了两种方式来 assert :一种是 assert 宏,另一种是预处理指令
// #error 。 前者在运行期起作用,而后
// 者是预处理期起作用。它们对模板都不好使,因为模板是编译期的概念。
// static_assert 关键字的使用方式如下:
}
template <class T> struct Check {
static_assert(sizeof(int) <= sizeof(T), "T is not big enough!");
};
// 类型推断和转发
void TypeInferenceAndForwarding() {}
// 一、模板实参推断
// 一个完美转发可以理解为:1.一般推断 + 2.引用折叠
// 1.函数调用进行类型推断
template <typename T> void functionA(T& Val) {} // 实参必须是一个左值
template <typename T> void functionB(const T& Val) {} // 实参可以接受一个右值
template <typename T> void functionC(T&& Val) {}
void DemoA()
{
// Note: functionA 中的调用使用实参所引起的类型作为模板参数的类型
int i = 0;
const int ci = 1;
functionA(i); // OK, i: int, T: int
functionA(ci); // OK, i: const int, T: const int
// functionA(5); // ERR, 必须是一个左值
}
static void DemoB()
{
// Note: functionB 的参数是 const &,与实参中的 const 无关。
// Note: functionB 函数参数都会被推断为 const int&
int i = 0;
const int ci = 1;
functionB(i); // OK, i: int, T: int
functionB(ci); // OK, i: const int,
// T: int
// (当函数参数本身是const时,T的类型推断的结果不会是一个const类型,const已经是函数参数类型的一部分)
functionB(5); // OK, 一个const &参数可以绑定到一个右值, T: int
}
static void DemoC()
{
functionC(78); // OK, 实参是一个 int 类型的右值,T: int
}
// 2. 引用折叠和右值引用参数
// Note:
// 不能将一个右值引用绑定到一个左值上。但是,C++语言在正常绑定规则之外定义了【两个例外规则】,允许这种绑定。
// Exception 1: 第一个例外规则影响右值引用参数的推断如何进行。
// 将一个【左值】(传递)-->
// 函数的右值引用参数【且】此右值引用指向模板类型参数,则模板类型 (推断)-->
// 实参的左值引用类型 故当调用 functionC(i)
// 时,T 为 int& 而非 int. 看起来就像是传递了: int& && 即 int&
// 的右值引用。
// 通常,我们不能(直接)定义一个引用的引用。但是,通过【类型别名】或通过【模板类型参数】间接定义是可以的。
// Exception 2: 上述中的【间接】定义的引用的引用,则形成【折叠】
// 折叠就只有两种情况:
// ①只有右值引用右值引用会折叠成一个右值引用,即T&&
//&& => T&& 其他均为 T& ②其他:T&,例如:T&&, T&&&
// Example: 将 1 的特殊推断和 2 的折叠结合起来则可以对一个左值调用
// functionC
// functionC(i) --> Line 41 --> T: int&
// 结合折叠规则 T& && ==> T& 推出结果为 int& 因此:即使 functionC
// 的函数参数形式是一个右值引用(即,T&&),此调用也会用一个左值引用类型(即,int&)实例化
// functionC
// 3.上述两个规则将导致两个重要结果:
// Result 1:
// 如果一个函数参数是一个指向模板类型参数的右值引用(如,T&&),则它【可以】被绑定到一个【左值】
// Result 2: 可以将任意类型的实参传递给T&&类型的函数参数。
// 4. 在实际中,右值引用通常用于两种情况:(模板转发其实参①)或(模板被重载②)。
// 二、std::move
// 从一个左值static_cast到一个右值引用是允许的。这是一条针对右值引用的特许规则:
// Notice:
// 不能隐式地将一个左值转换为右值引用,但我们可以用static_cast显式地将一个左值转换为一个右值引用。
// 三、转发
// 转发目的:某些函数需要将其一个或多个实参【连同类型不变地】转发给其他函数。
// (在此情况下,我们需要保持被转发实参的所有性质,包括实参类型是否是const的以及实参是左值还是右值。)
// 引出疑问:将模板参数设定为 T&& ,不就既可以接受 T& 也可以接受 T&&
// 吗,为什么还需要转发?
// 问题原因:上述规则只在模板推断时起作用,但是如果说调用一个函数,函数参数即便是
// T&& 类型,实际上也会变成左值,
// 因为:【函数参数与其他任何变量一样,都是左值表达式!】yyy
// 所以:将模板参数设定为
// T&&只能解决一半的问题,还需要别的方法就是【转发】
// 我们可以使用一个名为forward的新标准库设施来传递flip2的参数,它能保持原始实参的类型,类似move。
// 与move不同,forward必须通过显式模板实参来调用。
// forward返回该显式实参类型的右值引用。即,forward<T>的返回类型是T&&。
// 模板相关
void template_relate()
{
// 1.类模板,顾名思义,类的模板。模板类,是一个类,实例化的类。
}

52
cppbase/grammar/grammar.h Normal file
View File

@ -0,0 +1,52 @@
#pragma once
// const和static关键字
void ConstAndStatic();
// 很基础的部分
void basic();
// to binary progress
void to_binary_progress();
// C和C++的区别
void DiffBetweenCppAndC();
// memory
void memory_use();
// C++的四个类型转换
void FourTypeConvert();
// 指针和引用的区别
void PointerAndReference();
// volatile 和 extern 关键字
void VolaAndExtern();
// define 和 const
void DefineAndConst();
// 计算类的大小
void CalcClassSize();
// 对象相关
void class_relate();
// 面向对象的三大特性、封装,继承、多态
void EncapInheriAndPolyMorphism();
// lambda 表达式
void Lambda();
// auto 和 decltype
void AutoAndDecltype();
// 静态 assert
void StaticAssert();
// 类型推断和转发
void TypeInferenceAndForwarding();
// 模板相关
void template_relate();

15
cppbase/main.cpp Normal file
View File

@ -0,0 +1,15 @@
#include <iostream>
#include <string>
#include "MSkipList.hpp"
int main()
{
SkipList<int, std::string> skip(12);
skip.insert(45, "CPP");
std::string tmp{};
if (skip.search(45, tmp)) {
std::cout << tmp << "\n";
}
return 0;
}

8
cppbase/object.cpp Normal file
View File

@ -0,0 +1,8 @@
#include <iostream>
int main()
{
return 0;
}

View File

@ -0,0 +1,243 @@
#ifndef MSKIPLIST_HEADER
#define MSKIPLIST_HEADER
#include <atomic>
#include <cassert>
#include <random>
#include <cstring>
template <typename T, typename P>
class SkipList {
public:
// 插入数据
void insert(const T& key, const P& value);
// 删除数据
void remove(const T& key);
// 查询数据
bool search(const T& key, P& value);
// 是否包含某个数据
bool contains(const T& key);
// 当前表节点个数
std::size_t count() const;
// 清除表
void clear();
public:
SkipList(int max_level = 12);
~SkipList();
private:
struct SkipNode;
std::size_t count_{};
SkipNode** pre_{};
SkipNode* header_{};
const int max_level_{}; // 限定的最大高度
std::atomic_int cur_max_height_{}; // 当前使用的最大高度
std::random_device rd_{};
std::mt19937 gen_{};
std::uniform_int_distribution<int> dis_{};
private:
int random_level();
SkipNode* find_node(const T& key);
};
template <typename T, typename P>
SkipList<T, P>::~SkipList()
{
clear();
delete[] pre_;
delete header_;
}
template <typename T, typename P>
void SkipList<T, P>::clear()
{
SkipNode* start = header_->get_no_bar(0);
while (start) {
SkipNode* n = start;
start = start->get_no_bar(0);
delete n;
--count_;
}
}
template <typename T, typename P>
inline int SkipList<T, P>::random_level()
{
static const int base_ = 2;
int height = 1;
while (height < max_level_ && (dis_(gen_) % base_) == 0) {
++height;
}
return height;
}
template <typename T, typename P>
struct SkipList<T, P>::SkipNode {
T key_{};
P value_{};
int level_{};
public:
explicit SkipNode(int level = 1) { alloc(level); }
~SkipNode() { release(); }
SkipNode(const T& key, const P& value, int level)
{
key_ = key;
value_ = value;
alloc(level);
}
void alloc(int max_level)
{
if (max_level < 1) {
return;
}
release();
next_ = new std::atomic<SkipNode*>[max_level] {};
}
void release() { delete[] next_; }
SkipNode* get(int n)
{
assert(n >= 0);
return next_[n].load(std::memory_order_acquire);
}
SkipNode* get_no_bar(int n)
{
assert(n >= 0);
return next_[n].load(std::memory_order_relaxed);
}
void set(int n, SkipNode* node)
{
assert(n >= 0);
next_[n].store(node, std::memory_order_release);
}
void set_no_bar(int n, SkipNode* node)
{
assert(n >= 0);
next_[n].store(node, std::memory_order_relaxed);
}
private:
std::atomic<SkipNode*>* next_{};
};
template <typename T, typename P>
inline SkipList<T, P>::SkipList(int max_level)
: max_level_(max_level),
cur_max_height_(1),
gen_(rd_()),
dis_(0, std::numeric_limits<int>::max())
{
assert(max_level_ > 0);
header_ = new SkipNode(max_level_);
pre_ = new SkipNode*[max_level_];
}
template <typename T, typename P>
typename SkipList<T, P>::SkipNode* SkipList<T, P>::find_node(const T& key)
{
memset(pre_, 0x0, sizeof(SkipNode*) * max_level_);
SkipNode* x = header_;
int level = cur_max_height_.load() - 1;
while (true) {
SkipNode* next = x->get(level);
if (next && next->key_ < key) {
x = next;
} else {
pre_[level] = x;
if (level == 0) {
return next;
} else {
--level;
}
}
}
}
template <typename T, typename P>
inline bool SkipList<T, P>::contains(const T& key)
{
SkipNode* x = find_node(key);
if (x && x->key_ == key) {
return true;
}
return false;
}
template <typename T, typename P>
inline std::size_t SkipList<T, P>::count() const
{
return count_;
}
template <typename T, typename P>
inline void SkipList<T, P>::insert(const T& key, const P& value)
{
SkipNode* x = find_node(key);
if (x && x->key_ == key) {
x->value_ = value;
return;
}
int height = random_level();
if (height > cur_max_height_) {
for (int i = cur_max_height_; i < height; ++i) {
pre_[i] = header_;
}
cur_max_height_.store(height, std::memory_order_relaxed);
}
x = new SkipNode(key, value, height);
for (int i = 0; i < height; ++i) {
x->set_no_bar(i, pre_[i]->get_no_bar(i));
pre_[i]->set(i, x);
}
++count_;
}
template <typename T, typename P>
inline void SkipList<T, P>::remove(const T& key)
{
memset(pre_, 0x0, sizeof(SkipNode*) * max_level_);
SkipNode* x = header_;
SkipNode* purpose = nullptr;
int level = cur_max_height_.load() - 1;
while (true) {
if (level < 0) {
break;
}
SkipNode* next = x->get(level);
if (!next) {
--level;
continue;
}
if (next->key_ < key) {
x = next;
continue;
}
if (next->key_ == key) {
SkipNode* nx = next->get_no_bar(level);
x->set_no_bar(level, nx);
purpose = next;
}
--level;
}
delete purpose;
--count_;
}
template <typename T, typename P>
inline bool SkipList<T, P>::search(const T& key, P& value)
{
SkipNode* x = find_node(key);
if (x && x->key_ == key) {
value = x->value_;
return true;
}
return false;
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@ -0,0 +1,64 @@
《核心方向》
参考别人代码,纵向是一个列表使用层索引推动,横向用各自元素的next指针往右侧推动。
《问题1:如果后来的节点随机的层数比最左侧高该怎么弄?》
目前看来,在初始化的时候就限定了最高层,所以随机不让超过此层。
《问题2:假如第二个节点很矮,是不是这样,在后边就回不到高处了》
不是必须经过矮点,可以直接 next 到后面高点。
《问题3:底层节点是有序的,比如最开始空数据时,先来一个8节点,又来个2节点,2怎么插到8前面去》
个人觉得就是找节点找到空的就往前面插入。
《atomic 相关》
1.std::memory_order_releaxed
这种语义表明加载操作并不引入额外的同步或顺序性,因此不会有额外的同步开销。
2.std::memory_order_acquire
这种语义确保了对被加载数据的后续操作符合所加载的数据顺序,即在加载数据后,
后续对返回的节点数据操作会被保证在加载操作之后执行。
3.std::memory_order_release
这种语义通常用于写操作,以确保在写入某个变量后,所有之前的写入操作在此之前都已完成。
《Leveldb中遇到的一个可变长结构体》
struct A { int a; char b[1];}
其中b其实是可变长的结构体,也叫柔性数组(注意b必须在结构体最后面),
实际分配内存时,给b动态分配额外的内存空间,可以使用[i]形式访问。
如果定义成 char* 那么额外的空间将不能被访问到,因为malloc的数据内容是未定义的,
为什么 char* data = (char *)malloc(4); 可以使用data[i]访问,是因为data存的是已经分配好的空间的地址,
而直接给 A malloc内存,如果定义成 char* b就如上面所说。
一.与传统指针的区别
1.b[1]的形式在编译器就预留了一定的空间,在简单足够使用时可以避免new free的额外管理操作。
2.这样写能一眼看出这个变量是一个数组。
《new 的用法》
new 可以在已经分配的空间上调用构造:
new (memory_alloc_addr) A(1);
《思路的改进点》
我默认的思路是同一个值的一个链条,结构体这样定义:
struct N {
T* k;
N* next;
};
k指向一个有真正内存的首个节点,然后再定义个下一个位置指针,
github 源码中的思路是一个结构体
struct N {
T k;
N* next; // 这个next是一个数组
}
这样一来,同一个值的数据,一个 N 就可以了,剩余的都是指针。相比较默认思路的优势是
可以直接使用下表 [i] 访问而无需一个一个next,速度更快,且在后续直接取N某一层节点数
据的逻辑中直接用,用默认next的方法,多很多不必要的逻辑。
《leveldb中跳表的实现逻辑》
一、这里说一下插入整体逻辑,这个逻辑通了,别的都OK。
1.一个数据一个 Node,该 Node 中含有一个 Node* 列表用于延伸高度。
2.在查找某个数据时,定义了一个高度的 Node* 临时列表 pre,用于记录每一层的插入点在哪个 Node。
3.pre 找好之后,new 一个保存该值的 Node x,
4.将 pre 待插入节点的下一级节点放到 x 下一级,pre 下一级改到 x。
二、这里说一下插入的细节逻辑
1.默认无数据时候,有个 Head 作用的 Node。
2.随机的高度比当前数据中的最高要高时,要将 pre 在该层的记录点指向 head

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

6
main.cpp Normal file
View File

@ -0,0 +1,6 @@
#include <iostream>
int main()
{
return 0;
}