Week 2 Day 2 代码挑战:实现自定义字符串类(支持移动语义)

华为OD机试真题 2026.11.4. 新系统 【河流水系流量评估】 华为OD机试真题 新系统【河流水系流量评估】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【河流水系流量评估】多语言题解。 阅读详情

挑战目标

深入理解现代C++的类设计原则、拷贝控制与移动语义。通过实现一个支持移动语义的自定义字符串类,你将掌握:

  1. Rule of Five(五大特殊成员函数)的正确实现
  2. 深拷贝与浅拷贝的区别与应用
  3. 移动语义的原理与性能优势
  4. 异常安全保证在资源管理类中的重要性
  5. 动态内存管理的正确模式

具体要求

基础功能要求

  1. 实现完整的 String 类,支持动态内存管理与异常安全
  2. 实现五大特殊成员函数(Rule of Five):
    • 默认构造函数
    • 拷贝构造函数
    • 移动构造函数
    • 拷贝赋值运算符
    • 移动赋值运算符
    • 析构函数
  3. 支持常用字符串操作:
    • 从C风格字符串构造
    • 获取字符串长度
    • 获取C风格字符串表示
    • 字符串拼接操作
  4. 正确处理边界情况:
    • 空字符串处理
    • 自赋值检测
    • 异常安全保证

接口设计参考

class String {
private:
    char* data;
    size_t length;
    
    // 辅助函数:分配内存并拷贝字符串
    void allocate_and_copy(const char* str, size_t len);
    
public:
    // 1. 默认构造函数
    String();
    
    // 2. 从C字符串构造
    explicit String(const char* str);
    
    // 3. 拷贝构造函数
    String(const String& other);
    
    // 4. 移动构造函数
    String(String&& other) noexcept;
    
    // 5. 拷贝赋值运算符
    String& operator=(const String& other);
    
    // 6. 移动赋值运算符
    String& operator=(String&& other) noexcept;
    
    // 7. 析构函数
    ~String();
    
    // 8. 获取长度
    size_t size() const;
    
    // 9. 获取C字符串
    const char* c_str() const;
    
    // 10. 字符串拼接
    String operator+(const String& other) const;
};

难度分级

基础版(必需完成)

完成上述所有基础功能要求,并通过以下测试:

  • 构造、拷贝、移动语义正确性
  • 内存泄漏检测通过
  • 基本字符串操作功能正常

进阶版(挑战提升)

在基础版基础上,增加以下功能:

  1. 实现拷贝交换习惯用法(Copy-and-Swap Idiom)
  2. 支持迭代器接口:

    cpp

    // 迭代器支持
    char* begin();
    const char* begin() const;
    char* end();
    const char* end() const;
    

  3. 实现更多字符串操作:
    • 下标访问运算符 operator[]
    • 字符串比较操作符 operator==、operator!=
    • 子串查找功能

  4. 添加小字符串优化(SSO):对于短字符串(如长度≤15),直接存储在对象内部,避免堆分配
  5. 性能优化:使用移动语义优化字符串拼接操作

参考实现要点

1. 内存管理核心逻辑

cpp

void String::allocate_and_copy(const char* str, size_t len) {
    data = new char[len + 1];  // 多分配一个字节存放'\0'
    std::memcpy(data, str, len);
    data[len] = '\0';
    length = len;
}

2. 移动语义实现关键

cpp

// 移动构造函数
String::String(String&& other) noexcept 
    : data(other.data), length(other.length) {
    // 转移资源所有权后,将源对象置为安全状态
    other.data = nullptr;
    other.length = 0;
}

// 移动赋值运算符
String& String::operator=(String&& other) noexcept {
    if (this != &other) {
        delete[] data;          // 释放当前资源
        data = other.data;      // 接管资源
        length = other.length;
        other.data = nullptr;   // 置空源对象
        other.length = 0;
    }
    return *this;
}

3. 异常安全保证

  • 基本异常安全:任何异常发生后,对象仍处于有效状态
  • 强异常安全:拷贝赋值使用拷贝交换习惯用法
  • 析构函数不抛出异常:确保资源正确释放

4. 拷贝交换习惯用法示例

cpp

// 友元交换函数
friend void swap(String& first, String& second) noexcept {
    using std::swap;
    swap(first.data, second.data);
    swap(first.length, second.length);
}

// 使用拷贝交换的拷贝赋值运算符
String& operator=(const String& other) {
    String temp(other);  // 拷贝构造(可能抛出异常)
    swap(*this, temp);   // 交换资源(不抛出异常)
    return *this;        // temp离开作用域,释放旧资源
}

测试用例示例

基础测试用例

cpp

#include <cassert>
#include <cstring>
#include <utility>

void test_string_basic() {
    // 测试1:默认构造
    String s1;
    assert(s1.size() == 0);
    assert(std::strcmp(s1.c_str(), "") == 0);
    
    // 测试2:C字符串构造
    String s2("Hello");
    assert(s2.size() == 5);
    assert(std::strcmp(s2.c_str(), "Hello") == 0);
    
    // 测试3:拷贝构造
    String s3 = s2;
    assert(s3.size() == 5);
    assert(std::strcmp(s3.c_str(), "Hello") == 0);
    // 验证深拷贝:修改s2不应影响s3
    // (需要通过其他方式验证,如查看内存地址)
    
    // 测试4:移动构造
    String s4 = std::move(s2);
    assert(s4.size() == 5);
    assert(std::strcmp(s4.c_str(), "Hello") == 0);
    // 验证移动后源对象状态
    assert(s2.size() == 0);
    assert(s2.c_str()[0] == '\0');
    
    // 测试5:拷贝赋值
    String s5;
    s5 = s3;
    assert(s5.size() == 5);
    assert(std::strcmp(s5.c_str(), "Hello") == 0);
    
    // 测试6:移动赋值
    String s6("World");
    s6 = std::move(s3);
    assert(s6.size() == 5);
    assert(std::strcmp(s6.c_str(), "Hello") == 0);
    
    // 测试7:字符串拼接
    String s7("Hello ");
    String s8("World");
    String s9 = s7 + s8;
    assert(s9.size() == 11);
    assert(std::strcmp(s9.c_str(), "Hello World") == 0);
    
    // 测试8:自赋值安全性
    s9 = s9;
    assert(s9.size() == 11);  // 自赋值后状态不变
    
    // 测试9:空字符串处理
    String s10("");
    assert(s10.size() == 0);
    String s11 = s10 + String("test");
    assert(s11.size() == 4);
    
    std::cout << "所有基础测试通过!" << std::endl;
}

内存泄漏检测建议

使用Valgrind或AddressSanitizer检查内存管理:

bash

# 使用Valgrind检测
valgrind --leak-check=full ./test_string

# 或使用gcc/clang的AddressSanitizer
g++ -fsanitize=address -g test_string.cpp -o test_string

扩展思考题

问题1:异常安全等级分析

你的String类实现了哪种异常安全保证?

  • 无异常安全:异常可能导致资源泄漏或数据损坏
  • 基本异常安全:异常后对象仍有效,但可能不是之前的状态
  • 强异常安全:异常后对象状态完全不变

如何改进以达到强异常安全级别?

问题2:性能优化策略

比较以下两种字符串拼接实现的性能差异:

cpp

// 方案A:直接实现
String operator+(const String& other) const {
    String result;
    size_t new_len = length + other.length;
    result.data = new char[new_len + 1];
    if (data) std::memcpy(result.data, data, length);
    if (other.data) std::memcpy(result.data + length, other.data, other.length);
    result.data[new_len] = '\0';
    result.length = new_len;
    return result;  // 可能触发移动构造
}

// 方案B:使用移动语义优化
String operator+(const String& other) && {  // 右值引用限定符
    // 如何优化?
}

如何为右值对象优化拼接操作,避免不必要的内存分配?

问题3:小字符串优化设计

设计小字符串优化(SSO)方案:

  • 如何检测字符串长度决定存储策略?
  • 对象内存布局如何设计?
  • 如何保证接口一致性?

问题4:现代C++特性应用

如何用C++17/20特性改进String类?

  • 使用std::string_view作为参数类型
  • 应用constexpr和consteval
  • 使用三路比较运算符<=>(C++20)

提交要求

  1. 完成String类的完整实现
  2. 编写测试用例验证所有功能
  3. 确保无内存泄漏(通过Valgrind检测)
  4. 记录实现过程中的关键决策与困难点
  5. 尝试回答至少2个扩展思考题

时间安排:建议1小时内完成基础版,额外时间挑战进阶功能。

验收标准:

  • 所有基础测试用例通过
  • Valgrind检测无内存泄漏
  • 移动语义正确实现(源对象状态正确重置)
  • 代码符合现代C++规范(Rule of Five)

相关资源

  1. 经典书籍参考:

    • 《Effective C++》条款5-12:构造/析构/赋值操作
    • 《Effective Modern C++》条款17:特殊成员函数生成
    • 《C++ Primer》第13章:拷贝控制

  2. 在线文档:

  3. 代码示例:

    • src/week2/class_design.cpp - 类设计基础示例
    • src/week2/string_impl.cpp - 字符串类参考实现

祝挑战顺利!通过这个练习,你将建立起面向对象设计的坚实基石。 

一种解决STM32多串口同时收发的方法 在做项目中,遇到了同时调用串口通信时程序崩溃的问题,在项目中,串口1用作调试串口,串口2用作MQTT通信串口,串口3用作下位机通信串口, 串口1重定向以后,用库函数自带的printf函数打印字符串 串口2使用自己写的u2_printf函数,即va_list这套变参宏定义后使用vsprintf函数打印字符串 串口3刚开始使用串口2一样的打印方式,程序在调试调用到串口3打印函数时直接崩溃,后续将串口3改用dma转存输出,仍然无法正常调用 多次研究以后,采用三个串口都使用重定向后的库函数自带的printf 阅读详情

相关推荐

BLDC方波控制双闭环Simulink仿真模型

无刷直流电机方波控制双闭环Simulink仿真模型搭建详细教程,包括速度PI环、电流PI环、换向逻辑和受控电压源,仿真结果良好。

m0_57458432的博客 6869

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 275

基于深度学习的UWB门禁检测

针对基于UWB技术智能门禁管理系统,由于测距误差或非视距原因导致门禁产生不合理动作的现象。分析如何采用基于深度学习的方法,通过对TWR测距数据、TWR监听数据、到达角度差(PDOA)、信道脉冲响应(CIR)等数据维度进行深度学习,利用 TensorFlow建立相应的推导模型,对卡片姿态进行更加精准可靠的检测,从而更好完成闸机开合控制。

tenth_quadrant的博客 2026

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 232

如何在huggingface上申请下载使用llama2/3模型

huggingface-cli download --resume-download 模型名称 --local-dir 存放模型的位置 --local-dir-use-symlinks False --resume-download --token token序列号xxxxxxx。登录huggingface,在模型详情页面上,找到这个表单,填写内容,提交申请。需要使用梯子,country填写梯子的位置吧(比如美国)等待一小时左右,会有邮件通知。

a61022706的博客 1万+

Understand-Anything:AI驱动的代码知识图谱,实现项目全局洞察与高效导航

在软件开发中,理解复杂代码库的结构和依赖关系是提升开发效率的关键。传统代码阅读方式往往陷入局部视角,难以快速把握系统全貌。知识图谱技术通过将代码实体(如文件、类、函数)及其关系(调用、继承、依赖)可视化,为代码理解提供了全局视角。这项技术的核心价值在于将静态代码转化为动态、可交互的认知地图,极大降低了系统学习与维护成本。在实际工程中,它广泛应用于新成员快速入职、遗留系统梳理、架构影响分析等场景。本文介绍的Understand-Anything工具,正是基于这一理念,通过多智能体分析管道,自动构建代码的结构图

weixin_42615290的博客 882

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 346

单片机按键识别篇---单击---双击----长按

  最近做一任务需要使用一个按键实现三种功能,分别是按键单击功能,按键双击功能和按键长按功能,可能之前没有接触过这类按键复用情况,顶多也只是简单识别单击和长按,没有想过双击以上按键功能,也是绞尽脑汁,想了半天,又上网看了些例程,算是对于按键的识别有点小小的了解,感觉这个功能挺实用,在此做个随笔。 一、思路   1.识别长按操作思路:我们使用系统定时器定时,然后让系统每过一段时间去扫描按键I/O...

weixin_30311605的博客 3660

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 125

从TCGA官网获取新版count数据并精准转换为TPM的实战指南

本文详细介绍了从TCGA官网获取新版count数据并精准转换为TPM的实战指南。通过TCGAbiolinks工具下载原始count数据,结合GENCODE v36注释文件计算基因有效长度,最终实现count到TPM的标准化转换,确保数据完整性和分析可靠性。文章包含完整代码示例和常见问题解决方案,特别适合生物信息学研究人员参考。

weixin_27310417的博客 283

OpenGL 进阶系列10 - 光线跟踪实现简介(Ray Tracing)

OpenGL 实现光线跟踪(Ray Tracing)是一个相对复杂但有趣的挑战,通常用于生成高度逼真的图像效果,如反射、折射、阴影等。OpenGL 本身并不是为光线跟踪设计的,但可以通过结合计算着色器和现代 OpenGL 功能实现光线跟踪。光线跟踪的基本思路是从相机(或视点)出发,发射光线并计算这些光线与场景中物体的交点。光线与场景中物体相交的计算。计算交点的光照(基于表面法线、光源位置、材质等)。反射和折射的处理(如果物体是透明或反射的)。

GPU全栈博主 1534

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 196

RK3568 Android14 移远GNSS卫星导航模组LC760Z GPS北斗定位模块调试

Rockchip RK3568 作为一款高性能、低功耗的国产SoC,被广泛应用于工业网关、车载中控和智能手持终端等领域。在这些应用场景中,位置服务(Location Service) 往往是不可或缺的功能。本文将详细记录如何集成GPS模组到 RK3568 开发板上,通过 UART(串口)接口集成通用的 GPS 模块(如 移远LC760Z 或 EC20 4G 模块自带的 GPS 功能也差不多),并实现数据的解析与上层应用对接。

qq_33427070的博客 1413

LeetCode Hot 100 二叉树专题(二)3 题(Java)

本篇文章共 3 题:543. 二叉树的直径、102. 二叉树的层序遍历、108. 将有序数组转换为二叉搜索树。 每道题都统一按「① 题目分析 & 解题思路 → ② 常见解法(附代码)及讲解 → ③ 知识点总结」的顺序组织;代码使用 Java 编写,节点定义沿用 LeetCode 默认的 TreeNode。

2401_88612756的博客 365

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Decorator

TaskDecorator 是异步场景下"跨线程传递上下文"的标准方案,主要用于传递 MDC、用户信息、数据源路由等上下文数据,核心价值是让异步任务的日志链路不中断、业务上下文不丢失。TaskDecorator 是 Spring 提供的一个钩子接口,用于在异步任务执行前对 Runnable 进行包装,常用于跨线程传递上下文。如果你在项目中遇到具体的异步上下文传递问题,可以把代码贴出来,我帮你给出最合适的 Decorator 方案 👍。✅ 核心能力:在线程切换时,把"父线程的上下文"搬到"子线程"

wlwork66的博客 106

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

JDK 下载安装:装完 Java 提示「不是内部或外部命令」?环境变量没配

JDK 是写 Java 程序必须装的东西。它包含运行 Java 的虚拟机(JRE)和编译 Java 的编译器(javac)——装了它,能跑、.java文件能编译成可执行程序。免费,学习和小规模生产都不花钱。

科小怪的博客 630

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C++ 多态详解:作用、原理与实战用法

多态是C++面向对象的核心特性,通过虚函数实现运行期动态绑定,使同一接口在不同对象上表现出不同行为。其核心作用包括统一接口、提升可扩展性、降低耦合、支持运行时分派与批量处理。实现依赖虚函数表(vtable)和虚指针(vptr),需注意虚析构函数、对象切片等陷阱。相比编译期多态(模板),运行期多态灵活性高但有调用开销。合理结合两者,可兼顾代码的灵活性与性能,是构建可扩展、低耦合系统的关键。

2402_85743155的博客 382

java框架大全

java框架大全

qq_39326137的博客 183

Java 多态 学习笔记

多态是面向对象编程的重要特性,指同一操作作用于不同对象时产生不同行为。其核心在于“父类引用指向子类对象”,结合方法重写实现运行时动态绑定。多态使代码更灵活、可扩展,如用Person p = new Student();统一处理各类人员注册,无需修改调用逻辑。但多态无法直接调用子类特有方法,需通过instanceof判断并强制转换。编译时看左边(类型),运行时看右边(实际对象),实现解耦与维护便利。

AA_AA114514的博客 358

SecureRandom做随机源,这个Java随机字符串工具类有3个方法

本文分享一款JDK17环境下的Java随机字符串工具类,内置3个开箱即用的方法,全部采用SecureRandom加密安全随机源,不存在弱随机、可预测、取模偏差等问题。工具类支持默认62字符集随机串、纯数字随机串、自定义字符集随机串三种生成模式,支持Unicode码点级别的字符集处理,兼容Emoji等增补字符,边界处理严格,异常信息明确,适合验证码、邀请码、盐值、随机口令、短链码等场景,源码可直接复制使用。

Java后端 | AI智能体应用实战派 | 电脑技能行家 298

Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

【代码】Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

m0_64476639的博客 326

ubuntu安装hadoop系统

本文详细介绍了基于VMware克隆虚拟机搭建三节点Hadoop分布式集群的全过程:使用同一Ubuntu镜像克隆出master、worker1、worker2三台虚拟机,依次配置主机名、固定IP(如192.168.82.10/11/12)、SSH服务及免密登录,确保节点间网络互通。随后安装Java,下载配置Hadoop并分发至各节点,最终初始化启动集群,验证其存储与计算功能,实现高效的大数据环境部署。

qq_43592674的博客 329

Agent 状态栏在上下文中的具体位置

Agent状态栏以user角色消息形式追加至上下文末尾,而非修改system消息,避免破坏KVCache。此举仅为API技术实现,内容由框架自动生成,非真实用户输入,通过<agent_status>标签标识,确保模型获取最新状态且不影响缓存。该设计遵循“动态信息追加、静态信息固定”原则,实现高效、低延迟的状态传递。

lj1109053360的专栏 72

Mac上装了最新版本的Android Studio,但是没找到jdk的设置选项也没找到jbr的目录

新版Android Studio(2025.3.1+)因三项变更导致常见问题:一、JDK设置移至“DaemonJVM criteria”,新项目默认自动探测或下载JDK;二、JBR目录改名为带版本号的jbr-21等,需进入.app/Contents/查看,或通过About窗口确认运行时;三、命令行缺adb因platform-tools未加入PATH,需在SDK Manager中勾选并配置~/.zshrc中的ANDROID_HOME及PATH。重装或清理残留可解决缺失问题,切勿手动设JAVA_HOME或安装冲

Programming is a highly artistic endeavor 348

Spring事务和事务传播机制

3. 可重复读(REPEATABLE READ): 事务不会读到其他事务对已有数据的修改, 即使其他事务已提交. 也就可以确保同⼀事务多次查询的结果⼀致, 但是其他事务新插⼊的数据, 是可以感知到的. 这也就引发了幻读问题. 可重复读, 是 MySQL 的默认事务隔离级别.p1事务可以认为是⽗事务, 嵌套事务是⼦事务. ⽗事务出现异常, ⼦事务也会回滚, ⼦事务出现异常, 如果不进⾏处理, 也会导致⽗事务回滚.

txxzjmzlh的博客 205

Java Arrays.asList 常见坑详解|UnsupportedOperationException、数组联动、基本类型 bug

解析 Arrays.asList 常见开发陷阱,演示 UnsupportedOperationException、底层数组引用共享、基本类型数组问题,区分 Arrays 内部 ArrayList 与 java.util.ArrayList,给出数组转 List 的各种场景正确代码。

weixin_42617033的博客 470
上一篇: Week 2 Day 1:类与对象设计原则
下一篇: Week 2 Day 3:继承与多态机制定制执行简报
phaethon.wu
博客等级 码龄5年 0粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值