project0
一. 前置知识
1 函数返回值类型(右值or左值)

2 std::unique_ptr
std::unique_ptr管理对象的析构

1.std::unique_ptr对象析构
2.改变原有std::unique_ptr的管理对象
方法一:

方法二:

可移动不可复制

对上图内容的举例说明:
std::unique_ptr 是 C++11 引入的智能指针之一,用来管理动态分配的对象,确保对象的生命周期由 unique_ptr 来管理,自动释放内存,避免内存泄漏。
- 空的
unique_ptr
unique_ptr可以指向一个对象,也可以不指向任何对象,即为空。当unique_ptr为空时,它不会占用任何资源。我们可以使用std::unique_ptr<T> ptr;来定义一个空的unique_ptr,它不管理任何资源。
示例:
std::unique_ptr<int> ptr; // 空的 unique_ptr
- 如果在声明时没有初始化对象,
ptr就是空指针。
非空的 unique_ptr:
std::unique_ptr<int> ptr = std::make_unique<int>(10); // ptr 管理一个动态分配的 int 对象
- 管理单个对象
std::unique_ptr常用于管理单个对象,例如通过new运算符动态分配的对象。当unique_ptr被销毁时,它会自动释放对象的内存,避免手动调用delete。
示例:
std::unique_ptr<int> ptr = std::make_unique<int>(10); // 管理一个动态分配的 int 对象
std::cout << *ptr << std::endl; // 输出 10
- 这里,
std::make_unique<int>(10)分配了一个int类型的对象,且初始化为10。当ptr离开作用域时,它会自动释放内存。
- 管理动态分配的对象数组
std::unique_ptr也可以用于管理动态分配的数组。使用new[]来分配数组时,我们可以使用unique_ptr来管理该数组。
示例:
std::unique_ptr<int[]> arr = std::make_unique<int[]>(5); // 管理动态分配的数组
arr[0] = 10;
arr[1] = 20;
std::cout << arr[0] << ", " << arr[1] << std::endl; // 输出 10, 20
- 这里,
std::make_unique<int[]>(5)分配了一个int类型的数组,且数组大小为 5。当arr离开作用域时,数组的内存会自动释放。
std::unique_ptr的可移动性
std::unique_ptr是可移动构造(MoveConstructible)和可移动赋值(MoveAssignable)的。意味着可以将一个unique_ptr的所有权从一个对象转移到另一个对象,但是不能拷贝它们。
示例:
std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
std::unique_ptr<int> ptr2 = std::move(ptr1); // ptr1 的所有权转移给 ptr2
std::cout << *ptr2 << std::endl; // 输出 10
// ptr1 现在是空的,不能再访问它
- 在这个例子中,
ptr2获取了ptr1管理的对象的所有权,ptr1成为了空指针。由于unique_ptr不允许复制,因此不能使用赋值语句如ptr2 = ptr1;,只能通过std::move进行所有权转移。
- 不支持复制构造和复制赋值
std::unique_ptr不允许进行复制构造(CopyConstructible)和复制赋值(CopyAssignable)。这意味着不能将一个unique_ptr赋值给另一个unique_ptr,因为这会导致两个unique_ptr同时管理同一个对象,进而导致资源释放时的重复释放(双重释放)。
示例:
std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
// std::unique_ptr<int> ptr2 = ptr1; // 编译错误,不能复制
- 这里的代码会编译失败,因为
unique_ptr不支持复制构造和复制赋值。
T不是合法类型
- 如果
T不是合法类型(例如T是引用类型),则std::unique_ptr<T>是非法的。unique_ptr只能管理指向对象的指针,而不能管理引用类型或其他不符合要求的类型。
非法示例:
// 编译错误,引用类型不能被 unique_ptr 管理
std::unique_ptr<int&> ptr; // 错误,不能管理引用
- 在这个例子中,
std::unique_ptr<int&>无法编译,因为std::unique_ptr不能管理引用类型(int&是引用类型,不是对象类型)。
总结:
std::unique_ptr用于管理动态分配的单个对象或数组。- 它支持可移动构造和赋值,但不支持拷贝构造和拷贝赋值。
- 它在超出作用域时会自动释放资源,从而避免内存泄漏。
- 不能管理引用类型或其他不合法的类型。
图三:

对上图的解释:
std::unique_ptr 详细解释
std::unique_ptr 是 C++11 引入的智能指针,专门用于管理动态分配的对象(通常通过 new 或 new[] 创建)。它有以下特点和用途:
- 非
const的std::unique_ptr可以转移所有权
-
std::unique_ptr是独占的智能指针。只有非const的unique_ptr能转移它所管理的对象的所有权。当unique_ptr是const时,它无法转移所有权,因为它不能修改所管理的对象(转移之后就被置空了,相当于改变了管理的对象)。 -
示例:
std::unique_ptr<int> ptr1 = std::make_unique<int>(10); std::unique_ptr<int> ptr2 = std::move(ptr1); // 正常转移所有权 // std::unique_ptr<int> ptr3 = ptr2; // 编译错误,不能复制-
在这个示例中,
ptr1的所有权被std::move转移给了ptr2。ptr1成为一个空指针,无法继续访问其原始对象。 -
如果
ptr1是const,则无法转移所有权:const std::unique_ptr<int> ptr1 = std::make_unique<int>(10); // std::unique_ptr<int> ptr2 = std::move(ptr1); // 编译错误,不能从 const 的 unique_ptr 转移所有权
-
std::unique_ptr提供异常安全性
-
std::unique_ptr确保在正常退出和异常退出时,都能自动释放资源。通过智能指针的生命周期管理,避免了手动调用delete,从而避免了内存泄漏问题。 -
示例:
void processData() { std::unique_ptr<int[]> arr = std::make_unique<int[]>(100); // 进行操作 if (someCondition) { throw std::runtime_error("Exception occurred"); } // 无论异常是否发生,arr 会在函数结束时自动释放 }- 即使在函数中发生了异常,
arr也会在函数返回前自动销毁,确保资源得到释放,提供异常安全性。

- 即使在函数中发生了异常,
- 传递和接收
std::unique_ptr的所有权
-
std::unique_ptr通过 转移所有权 来传递和接收动态分配对象。它不能被复制,但可以被移动。 -
示例:
std::unique_ptr<int> createObject() { return std::make_unique<int>(42); // 返回一个 unique_ptr,该返回值为右值 } void processObject(std::unique_ptr<int> ptr) { std::cout << *ptr << std::endl; // 处理传入的 unique_ptr } int main() { std::unique_ptr<int> ptr = createObject(); processObject(std::move(ptr)); // 转移所有权 // ptr 在这里变为空,不能再使用 }- 通过
std::move(ptr),ptr的所有权被转移到processObject函数中,ptr变为空,不再可以访问所管理的对象。
- 通过
std::unique_ptr用作容器元素
-
std::unique_ptr可以用作容器(例如std::vector)的元素类型。当需要多态行为或动态分配对象时,std::unique_ptr是一个常见的选择。 -
示例:
class Base { public: virtual void show() = 0; }; class Derived : public Base { public: void show() override { std::cout << "Derived class\n"; } }; int main() { std::vector<std::unique_ptr<Base>> vec; vec.push_back(std::make_unique<Derived>()); vec[0]->show(); // 输出 "Derived class" }- 在这个示例中,
std::vector<std::unique_ptr<Base>>用来管理一系列的动态分配对象。当Derived对象被销毁时,它会自动调用Derived类的析构函数,并释放内存。
- 在这个示例中,
std::unique_ptr与不完整类型(如pImpl)
-
std::unique_ptr可以用于管理不完整类型(例如pImpl手法中的opaque指针),从而可以隐藏实现细节。在pImpl手法中,通常将实现细节放在一个类的内部,并使用unique_ptr来管理它。 -
示例:
class MyClassImpl; // 不完整类型 class MyClass { public: MyClass(); ~MyClass(); private: std::unique_ptr<MyClassImpl> impl; // 管理不完整类型 };- 在这个示例中,
MyClassImpl是一个不完整类型,MyClass通过std::unique_ptr管理它的生命周期,隐藏了MyClassImpl的实现细节。 - 使用
std::unique_ptr管理不完整类型时,删除器会在析构时被调用,要求MyClassImpl必须是完整类型。
- 在这个示例中,
std::unique_ptr 与继承关系
-
如果
T是基类B的派生类,那么std::unique_ptr<T>可以隐式转换为std::unique_ptr<B>。这使得std::unique_ptr在面向对象设计中非常有用。但必须注意,如果基类的析构函数不是虚拟的,那么删除时可能会出现未定义行为。 -
示例:
class Base { public: virtual ~Base() = default; }; class Derived : public Base { public: ~Derived() override { std::cout << "Derived destructor\n"; } }; int main() { std::unique_ptr<Base> ptr = std::make_unique<Derived>(); // `ptr` 会正确调用 Derived 的析构函数 }-
在这个示例中,
std::unique_ptr<Base>管理了Derived类的对象。当ptr被销毁时,Derived类的析构函数会被调用,并释放内存。 -
如果基类的析构函数不是虚拟的,那么可能会导致未定义行为:
class Base { public: ~Base() { std::cout << "Base destructor\n"; } // 非虚拟析构函数 }; class Derived : public Base { public: ~Derived() override { std::cout << "Derived destructor\n"; } }; int main() { std::unique_ptr<Base> ptr = std::make_unique<Derived>(); // 销毁时 Base 的析构函数会被调用,Derived 的析构函数没有被调用 }- 这种情况下,
std::unique_ptr只会调用基类Base的析构函数,Derived类的析构函数不会被调用,导致资源没有被正确释放。
- 这种情况下,
-
- 定制删除器和非标准指针类型
-
与
std::shared_ptr不同,std::unique_ptr允许使用定制删除器来管理指针,例如通过boost::offset_ptr等自定义类型来管理指向共享内存的对象。 -
示例:
std::unique_ptr<int, void(*)(int*)> ptr(new int(10), [](int* p) { delete p; });- 这个示例中,
std::unique_ptr使用了一个自定义的删除器,通过 Lambda 表达式来删除int对象。
- 这个示例中,
总结
std::unique_ptr是一个独占式的智能指针,适用于动态内存管理。它能确保资源在作用域结束时自动释放,提供异常安全性。- 它可以通过
std::move转移所有权,但不支持复制。 - 它适用于管理基类指针、容器元素以及不完整类型等场景。
- 使用
std::unique_ptr时要小心继承关系,确保基类析构函数为虚拟析构函数以避免未定义行为。
例子:
#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <locale>
#include <memory>
#include <stdexcept>
// 用于下面运行时多态演示的辅助类
struct B
{
virtual ~B() = default;
virtual void bar() { std::cout << "B::bar\n"; }
};
struct D : B
{
D() { std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }
void bar() override { std::cout << "D::bar\n"; }
};
// 消费 unique_ptr 的函数能以值或以右值引用接收它
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
}
// 用于下面自定义删除器演示的辅助函数
void close_file(std::FILE* fp)
{
std::fclose(fp);
}
// 基于 unique_ptr 的链表演示
struct List
{
struct Node
{
int data;
std::unique_ptr<Node> next;
};
std::unique_ptr<Node> head;
~List()
{
// 循环按顺序销毁各列表节点,默认析构函数将会递归调用其 “next” 指针的析构函数,
// 这在足够大的链表上可能造成栈溢出。
while (head)
{
auto next = std::move(head->next);
head = std::move(next);
}
}
void push(int data)
{
head = std::unique_ptr<Node>(new Node{data, std::move(head)});
}
};
int main()
{
std::cout << "1) 独占所有权语义演示\n";
{
// 创建一个(独占)资源
std::unique_ptr<D> p = std::make_unique<D>();
// 转移所有权给 “pass_through”,而它再通过返回值将所有权转移回来
std::unique_ptr<D> q = pass_through(std::move(p));
// “p” 现在是已被移动的“空”状态,等于 nullptr
assert(!p);
}
std::cout << "\n" "2) 运行时多态演示\n";
{
// 创建派生类资源并通过基类指向它
std::unique_ptr<B> p = std::make_unique<D>();
// 动态派发如期工作
p->bar();
}
std::cout << "\n" "3) 自定义删除器演示\n";
std::ofstream("demo.txt") << 'x'; // 准备要读取的文件
{
using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
unique_file_t fp(std::fopen("demo.txt", "r"), &close_file);
if (fp)
std::cout << char(std::fgetc(fp.get())) << '\n';
} // 在此调用 “close_file()”(如果 “fp” 为空)
std::cout << "\n" "4) 自定义 lambda 表达式删除器和异常安全性演示\n";
try
{
std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr)
{
std::cout << "由自定义删除器销毁...\n";
delete ptr;
});
throw std::runtime_error(""); // “p” 是普通指针的情况下此处就会泄漏
}
catch (const std::exception&)
{
std::cout << "捕获到异常\n";
}
std::cout << "\n" "5) 数组形式的 unique_ptr 演示\n";
{
std::unique_ptr<D[]> p(new D[3]);
} // “D::~D()” 被调用 3 次
std::cout << "\n" "6) 链表演示\n";
{
List wall;
const int enough{1'000'000};
for (int beer = 0; beer != enough; ++beer)
wall.push(beer);
std::cout.imbue(std::locale("en_US.UTF-8"));
std::cout << "墙上有 " << enough << " 瓶啤酒...\n";
} // 销毁所有啤酒
}
可能的输出:
1) 独占所有权语义演示
D::D
D::bar
D::~D
2) 运行时多态演示
D::D
D::bar
D::~D
3) 自定义删除器演示
x
4) 自定义 lambda 表达式删除器和异常安全性演示
D::D
由自定义删除器销毁...
D::~D
捕获到异常
5) 数组形式的 unique_ptr 演示
D::D
D::D
D::D
D::~D
D::~D
D::~D
6) 链表演示
墙上有 1,000,000 瓶啤酒...
3 std::unordered_map
map里成员是std::pair<>()






二 实现
加锁, 由于锁不是利用RAII封装的,所以在return前都要释放锁,避免死锁
具体代码不展示了
2 获取Gradescope在线测试用例:
#pragma once
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
void GetTestFileContent() {
static bool first_enter = true;
if (first_enter) {
// 截取gradescope日志输出文件名
/*
std::vector<std::string> all_filenames = {
"/autograder/bustub/test/primer/grading_starter_test.cpp",
"/autograder/bustub/test/execution/grading_update_executor_test.cpp",
"/autograder/bustub/test/execution/grading_nested_loop_join_executor_test.cpp",
"/autograder/bustub/test/execution/grading_limit_executor_test.cpp",
"/autograder/bustub/test/execution/grading_executor_benchmark_test.cpp",
"/autograder/bustub/test/concurrency/grading_lock_manager_3_test.cpp",
"/autograder/bustub/test/buffer/grading_parallel_buffer_pool_manager_test.cpp",
"/autograder/bustub/test/buffer/grading_lru_replacer_test.cpp",
"/autograder/bustub/test/execution/grading_executor_integrated_test.cpp",
"/autograder/bustub/test/execution/grading_sequential_scan_executor_test.cpp",
"/autograder/bustub/test/concurrency/grading_lock_manager_1_test.cpp",
"/autograder/bustub/test/execution/grading_distinct_executor_test.cpp",
"/autograder/bustub/test/buffer/grading_buffer_pool_manager_instance_test.cpp",
"/autograder/bustub/test/concurrency/grading_lock_manager_2_test.cpp",
"/autograder/bustub/test/concurrency/grading_transaction_test.cpp",
"/autograder/bustub/test/buffer/grading_leaderboard_test.cpp",
"/autograder/bustub/test/container/grading_hash_table_verification_test.cpp",
"/autograder/bustub/test/concurrency/grading_rollback_test.cpp",
"/autograder/bustub/test/container/grading_hash_table_concurrent_test.cpp",
"/autograder/bustub/test/container/grading_hash_table_page_test.cpp",
"/autograder/bustub/test/concurrency/grading_lock_manager_detection_test.cpp",
"/autograder/bustub/test/container/grading_hash_table_leaderboard_test.cpp",
"/autograder/bustub/test/container/grading_hash_table_scale_test.cpp",
"/autograder/bustub/test/container/grading_hash_table_test.cpp",
"/autograder/bustub/test/execution/grading_aggregation_executor_test.cpp",
"/autograder/bustub/test/execution/grading_insert_executor_test.cpp",
"/autograder/bustub/test/execution/grading_delete_executor_test.cpp",
"/autograder/bustub/test/execution/grading_hash_join_executor_test.cpp"
"/autograder/bustub/test/execution/grading_sequential_scan_executor_test.cpp",
"/autograder/bustub/test/execution/grading_update_executor_test.cpp",
"/autograder/bustub/test/execution/grading_executor_test_util.h",
"/autograder/bustub/src/include/execution/plans/mock_scan_plan.h",
};
*/
std::vector<std::string> filenames = {
"/autograder/bustub/test/execution/grading_executor_integrated_test.cpp",
"/autograder/bustub/test/execution/grading_executor_benchmark_test.cpp",
};
std::ifstream fin;
for (const std::string &filename : filenames) {
fin.open(filename, std::ios::in);
if (!fin.is_open()) {
std::cout << "cannot open the file:" << filename << std::endl;
continue;
}
char buf[200] = {0};
std::cout << filename << std::endl;
while (fin.getline(buf, sizeof(buf))) {
std::cout << buf << std::endl;
}
fin.close();
}
first_enter = false;
}
}
使用方法:
-
将filenames内的路径更换(具体路径,当你上传一次后就能看到)
-
将上述代码插入要上传提交测试的代码中(比如p0_trie.h)
-
在任意函数中调用GetTestFileContent(),代码只会执行一次
-
可将获取的在线测试用例,放入本地测试文件中,随后可在本地debug
-
debug时可结合log使用
LOG_INFO("# Pages: %d", num_pages);
LOG_DEBUG("Fetching page %d", page_id);
三 测试结果展示:
本地测试:
去掉测试代码中的 DISABLED_ 前缀
cd build
make starter_trie_test
./test/starter_trie_test

在线测试:
登录 https://www.gradescope.com/ 注册 入口代码:PXWVR5 学校选 Carnegie Mellon University

四 相关资源
- https://15445.courses.cs.cmu.edu/fall2022 课程官网
- https://github.com/cmu-db/bustub Bustub Github Repo
- https://www.gradescope.com/ 自动测评网站 GradeScope,course entry code: PXWVR5
- https://discord.gg/YF7dMCg Discord 论坛,课程交流用

浙公网安备 33010602011771号