当前位置: 首页 > news >正文

安全编码课程 实验5 动态内存(3)

实验项目:任务3

任务1和任务2基础上改写 SecureBuffer 类,完成安全编程,使用 Valgrind/AddressSanitizer或其他代码检测工具检测问题:


手动编写函数,实现 SafeDelete(T& ptr)*

功能点:

清空内存内容(如 memset(ptr, 0, size))

安全释放 delete

自动置空,避免悬垂指针

防止重复释放

拷贝构造更改为移动构造

为 SecureBuffer 类增加移动构造函数和移动赋值运算符

使用智能指针优化代码,降低悬垂指针风险,彻底杜绝手动 delete,更安全

使用 std::unique_ptr 管理 SecureBuffer

用 std::shared_ptr 进行引用计数(新增)

引入容器存储 std::shared_ptr,进行资源管理

功能点

使用 std::vector<std::shared_ptr<SecureBuffer>> 统一管理多个 SecureBuffer

确保对象在所有引用结束后自动释放

输出容器内容,观察 shared_ptr 的引用计数


测试要求(利用任务1的打印方法打印出内存内容):

  1. 使用 SafeDelete 测试
  2. 调用SecureBuffer类的修改方法(在任务1中已经实现),修改对象,之后进行移动构造测试移动语义
  3. 使用 std::unique_ptr 进行内存管理
  4. 使用 std::shared_ptr 进行共享引用管理
  5. 使用 std::vector 容器存储 shared_ptr

提交:

1. 代码(c++)

2. 测试用例

3. 过程及结果截图

4. 文字分析,要求:回顾和内存管理相关的3次实验内容,可以从这个角度进行总结:

手动管理内存(new/delete包括构造析构和分配释放)-> SafeDelete

拷贝构造 -> 移动构造

裸指针 -> 智能指针(unique_ptrshared_ptr

容器

目录

实验项目:任务3

 实验步骤、实验结果及结果分析:

1. SafeDelete 实现

 初始实现

添加内存清空

显式调用析构函数

2. 移动语义实现

 添加移动构造函数和移动赋值运算符

 3. 智能指针使用

使用 std::unique_ptr

 使用 std::shared_ptr 和 std::make_shared

 4. 容器管理

 使用 vector 管理 shared_ptr

提交:

1. 代码(c++)

2. 测试用例

3. 过程及结果截图

4. 文字分析


 实验步骤、实验结果及结果分析:

// 原始代码
#include<iostream>
#include<cstring>
class SecureBuffer{
	private:
		char* m_data;
		size_t m_size;
	public:
		SecureBuffer(size_t size):m_size(size){
			m_data=new char[m_size];
			memset(m_data,0xCC,m_size);
		}
		~SecureBuffer(){
			memset(m_data,0x00,m_size);
			delete[] m_data;
		}
	void print_data(){
		for(size_t i=0;i<m_size;++i){
			std::cout<<std::hex<<(int)m_data[i]<<" ";
		}
		std::cout<<std::endl;
	}
	char* get_data(){
		return m_data;
	}
		
};
int main(){
	// 1
	SecureBuffer* buffer=new SecureBuffer(10);
	std::cout << "After construction: ";
	buffer->print_data();
	buffer->~SecureBuffer();
	std::cout << "After explicit destruction: ";
	buffer->print_data();
	// 2
	std::cout << "Trying to read after destruction: ";
	std::cout<<std::hex<<(int)buffer->get_data()[0]<<std::endl;
	std::cout << "Trying to modify after destruction: ";
	buffer->get_data()[0]=0xFF;
	buffer->print_data();
	// 3
	operator delete(buffer);
	std::cout << "After operator delete: ";
	std::cout << std::hex << (int)buffer->get_data()[0] << std::endl;	
}
#include <iostream>    
#include <cstring>    
#include <vector>    
#include <memory>  // 引入智能指针头文件
    
class SecureBuffer {    
private:
    std::unique_ptr<char[]> m_data;  // 使用智能指针管理内存
    size_t m_size;
    
public:
    // 构造函数
    SecureBuffer(size_t size) : m_size(size), m_data(std::make_unique<char[]>(m_size)) {
        std::memset(m_data.get(), 0xCC, m_size);
    }

    // 析构函数(无需手动释放内存)
    ~SecureBuffer() {
        std::memset(m_data.get(), 0x00, m_size);  // 清空内存为 0x00
    }

    // 打印数据
    void print_data() const {
        for (size_t i = 0; i < m_size; ++i) {
            std::cout << std::hex << (int)m_data[i] << " ";
        }
        std::cout << std::endl;
    }    
};
    
int main() {
    std::vector<std::unique_ptr<SecureBuffer>> buffers;  // 使用智能指针
    for (int i = 0; i < 10000; ++i) {
        buffers.push_back(std::make_unique<SecureBuffer>(10));  // 自动管理内存
    }
    std::cout << "Created 10000 SecureBuffer objects with smart pointers." << std::endl;

    return 0;  // 程序退出时,智能指针会自动释放内存
}

1. SafeDelete 实现

手动编写函数,实现 SafeDelete(T& ptr)*

功能点:

清空内存内容(如 memset(ptr, 0, size))

安全释放 delete

自动置空,避免悬垂指针

防止重复释放

初始实现

  • 自动置空指针,避免悬垂指针
  • 防止重复释放(通过检查指针是否为 null)
template<typename T>   
void SafeDelete(T*& ptr) {
    if (ptr != nullptr) {
        delete ptr;
        ptr = nullptr;
    }   
}

添加内存清空

template<typename T>   
void SafeDelete(T*& ptr) {
    if (ptr != nullptr) {
        // 清空内存内容
        memset(ptr, 0, sizeof(T));
        delete ptr;
        ptr = nullptr;
    }
}

显式调用析构函数

template<typename T>   
void SafeDelete(T*& ptr) {
    if (ptr != nullptr) {
        // 显式调用析构函数
        ptr->~T();
        // 清空内存内容
        memset(ptr, 0, sizeof(T));
        // 使用 operator delete 释放内存
        ::operator delete(ptr);
        ptr = nullptr;
    }
}

实现了 SafeDelete 模板函数,具有以下安全特性:

  • 清空内存内容(使用 memset)
  • 安全释放内存(先调用析构函数,再 operator delete)
  • 自动置空指针,避免悬垂指针
  • 防止重复释放(通过检查指针是否为 null)

2. 移动语义实现

拷贝构造更改为移动构造

为 SecureBuffer 类增加移动构造函数和移动赋值运算符

class SecureBuffer {   
public:
    SecureBuffer(size_t size) : size_(size), data_(new uint8_t[size]) {}
    
    ~SecureBuffer() {
        if (data_) {
            memset(data_, 0, size_);
            delete[] data_;
            data_ = nullptr;
            size_ = 0;
        }
    }
    
    // 禁用拷贝构造和拷贝赋值
    SecureBuffer(const SecureBuffer&) = delete;
    SecureBuffer& operator=(const SecureBuffer&) = delete;
    
private:
    size_t size_;
    uint8_t* data_;   
};   

 添加移动构造函数和移动赋值运算符

class SecureBuffer {   
public:
    // ... 原有构造函数和析构函数 ...
    
    // 移动构造函数
    SecureBuffer(SecureBuffer&& other) noexcept 
        : size_(other.size_), data_(other.data_) {
        other.size_ = 0;
        other.data_ = nullptr;
    }
    
    // 移动赋值运算符
    SecureBuffer& operator=(SecureBuffer&& other) noexcept {
        if (this != &other) {
            // 清理当前对象资源
            if (data_) {
                memset(data_, 0, size_);
                delete[] data_;
            }
            
            // 转移资源所有权
            size_ = other.size_;
            data_ = other.data_;
            
            // 置空源对象
            other.size_ = 0;
            other.data_ = nullptr;
        }
        return *this;
    }
    
private:
    size_t size_;
    uint8_t* data_;   
};   

为 SecureBuffer 类增加了:

  • 移动构造函数:转移资源所有权,将源对象置空
  • 移动赋值运算符:安全转移资源,先清理当前对象资源
  • 禁用拷贝构造和拷贝赋值(= delete)

 3. 智能指针使用

使用智能指针优化代码,降低悬垂指针风险,彻底杜绝手动 delete,更安全

使用 std::unique_ptr 管理 SecureBuffer

用 std::shared_ptr 进行引用计数(新增)

使用 std::unique_ptr

void demoUniquePtr() {
    // 创建 unique_ptr 管理的 SecureBuffer
    std::unique_ptr<SecureBuffer> buffer(new SecureBuffer(1024));
    
    // 转移所有权
    std::unique_ptr<SecureBuffer> buffer2 = std::move(buffer);
    
    // buffer 现在为空
    assert(buffer == nullptr);
    
    // 自动释放,无需手动调用 delete   
}   

 使用 std::shared_ptr 和 std::make_shared

void demoSharedPtr() {
    // 使用 make_shared 创建 shared_ptr
    auto buffer1 = std::make_shared<SecureBuffer>(1024);
    
    {
        // 共享所有权
        auto buffer2 = buffer1;
        
        // 引用计数为2
        assert(buffer1.use_count() == 2);
    }
    
    // buffer2 离开作用域,引用计数减为1
    assert(buffer1.use_count() == 1);
    
    // 自动释放,当最后一个引用消失时    
}    
  • 使用 std::unique_ptr 管理 SecureBuffer,确保独占所有权和自动释放
  • 使用 std::shared_ptr 实现引用计数,允许多个所有者共享资源
  • 使用 std::make_shared 创建 shared_ptr,更高效且安全

 4. 容器管理

引入容器存储 std::shared_ptr,进行资源管理

功能点

使用 std::vector<std::shared_ptr<SecureBuffer>> 统一管理多个 SecureBuffer

确保对象在所有引用结束后自动释放

输出容器内容,观察 shared_ptr 的引用计数

 使用 vector 管理 shared_ptr

void demoContainer() {
    std::vector<std::shared_ptr<SecureBuffer>> buffers;
    
    // 添加几个 SecureBuffer
    buffers.push_back(std::make_shared<SecureBuffer>(256));
    buffers.push_back(std::make_shared<SecureBuffer>(512));
    buffers.push_back(std::make_shared<SecureBuffer>(1024));
    
    // 观察引用计数
    auto buffer = buffers[0];
    std::cout << "Reference count: " << buffer.use_count() << std::endl; // 2
    
    {
        auto localCopy = buffer;
        std::cout << "Reference count: " << buffer.use_count() << std::endl; // 3
    }
    
    std::cout << "Reference count: " << buffer.use_count() << std::endl; // 2
    
    // 从容器中移除
    buffers.erase(buffers.begin());
    std::cout << "Reference count: " << buffer.use_count() << std::endl; // 1
    
    // 最后一个引用消失时自动释放    
}    

使用 std::vector<std::shared_ptr<SecureBuffer>> 统一管理多个 SecureBuffer:

  • 可以观察 shared_ptr 的引用计数变化
  • 确保对象在所有引用结束后自动释放
  • 演示了共享指针的使用场景

测试要求(利用任务1的打印方法打印出内存内容):

  1. 使用 SafeDelete 测试
  2. 调用SecureBuffer类的修改方法(在任务1中已经实现),修改对象,之后进行移动构造测试移动语义
  3. 使用 std::unique_ptr 进行内存管理
  4. 使用 std::shared_ptr 进行共享引用管理
  5. 使用 std::vector 容器存储 shared_ptr

提交:

1. 代码(c++)

#include <iostream>   
#include <memory>   
#include <vector>   
#include <cstring>
   
// SafeDelete 
template<typename T>   
void SafeDelete(T*& ptr) {
    if (ptr != nullptr) {
        ptr->~T();              // 显式调用析构函数
        std::memset(ptr, 0, sizeof(T));  // 清空内存内容
        ::operator delete(ptr);  // 使用 operator delete 释放内存
        ptr = nullptr;           // 置空指针
    }   
}
   
// SecureBuffer 类   
class SecureBuffer {   
public:
    SecureBuffer(size_t size) : size_(size), data_(new uint8_t[size]) {
        std::memset(data_, 0xCC, size_);  // 初始化填充 0xCC
    }
    ~SecureBuffer() {
        if (data_) {
            std::memset(data_, 0, size_);  // 安全清理
            delete[] data_;
            data_ = nullptr;
            size_ = 0;
        }
    }
    // 禁用拷贝构造和拷贝赋值
    SecureBuffer(const SecureBuffer&) = delete;
    SecureBuffer& operator=(const SecureBuffer&) = delete;
    // 移动构造函数
    SecureBuffer(SecureBuffer&& other) noexcept 
        : size_(other.size_), data_(other.data_) {
        other.size_ = 0;
        other.data_ = nullptr;
    }
    // 移动赋值运算符
    SecureBuffer& operator=(SecureBuffer&& other) noexcept {
        if (this != &other) {
            // 清理当前对象资源
            if (data_) {
                std::memset(data_, 0, size_);
                delete[] data_;
            }
            
            // 转移资源所有权
            size_ = other.size_;
            data_ = other.data_;
            
            // 置空源对象
            other.size_ = 0;
            other.data_ = nullptr;
        }
        return *this;
    }
    // 打印缓冲区内容(实验一中实现过)
    void print_data() const {
        for (size_t i = 0; i < size_; ++i) {
            std::cout << std::hex << (int)data_[i] << " ";
        }
        std::cout << std::endl;
    }
    // 修改缓冲区内容 
    // (实验一中memset(m_data,0xCC,m_size);这里写成一个方法)
    void modify_data(uint8_t value) {
        if (data_) {
            std::memset(data_, value, size_);
        }
    }
    size_t size() const { return size_; }
   
private:
    size_t size_;
    uint8_t* data_;   
};

2. 测试用例

// 测试用例   
int main() {
    // 1. 测试 SafeDelete
    {
        std::cout << "=== Testing SafeDelete ===" << std::endl;
        SecureBuffer* buffer = new SecureBuffer(5);
        buffer->print_data();  // 输出初始值(0xCC)
        buffer->modify_data(0xFF);
        buffer->print_data();  // 输出修改后的值(0xFF)
        SafeDelete(buffer);    // 安全删除
        std::cout << "After SafeDelete: " << (buffer == nullptr ? "Success" : "Failed") << std::endl;
    }
    // 2. 测试移动语义
    {
        std::cout << "\n=== Testing Move Semantics ===" << std::endl;
        SecureBuffer buffer1(5);
        buffer1.modify_data(0xAA);
        std::cout << "Buffer1 before move: ";
        buffer1.print_data();
        SecureBuffer buffer2 = std::move(buffer1);  // 移动构造
        std::cout << "Buffer2 after move: ";
        buffer2.print_data();
        std::cout << "Buffer1 after move (should be empty): " << buffer1.size() << std::endl;
    }
    // 3. 测试 std::unique_ptr
    {
        std::cout << "\n=== Testing std::unique_ptr ===" << std::endl;
        std::unique_ptr<SecureBuffer> uniqueBuffer = std::make_unique<SecureBuffer>(3);
        uniqueBuffer->modify_data(0x11);
        uniqueBuffer->print_data();
        // unique_ptr 会自动释放内存
    }
    // 4. 测试 std::shared_ptr
    {
        std::cout << "\n=== Testing std::shared_ptr ===" << std::endl;
        std::shared_ptr<SecureBuffer> sharedBuffer1 = std::make_shared<SecureBuffer>(4);
        sharedBuffer1->modify_data(0x22);
        sharedBuffer1->print_data();
        std::shared_ptr<SecureBuffer> sharedBuffer2 = sharedBuffer1;  // 共享所有权
        std::cout << "Use count: " << sharedBuffer2.use_count() << std::endl;
    }
    // 5. 测试 std::vector<std::shared_ptr<SecureBuffer>>
    {
        std::cout << "\n=== Testing std::vector<std::shared_ptr<SecureBuffer>> ===" << std::endl;
        std::vector<std::shared_ptr<SecureBuffer>> bufferVec;
        bufferVec.push_back(std::make_shared<SecureBuffer>(2));
        bufferVec.push_back(std::make_shared<SecureBuffer>(3));
        bufferVec.push_back(std::make_shared<SecureBuffer>(4));
        for (const auto& buf : bufferVec) {
            buf->print_data();
        }
    }
    return 0;   
}

3. 过程及结果截图

  • in use at exit: 0 bytes:程序结束时所有动态分配的内存均被释放。
  • 18 allocs, 18 frees:内存分配和释放完全匹配,没有泄漏。
  • ERROR SUMMARY: 0:没有非法内存访问、未初始化内存使用等问题。

4. 文字分析

要求:回顾和内存管理相关的3次实验内容,可以从这个角度进行总结:

手动管理内存(new/delete包括构造析构和分配释放)-> SafeDelete

拷贝构造 -> 移动构造

裸指针 -> 智能指针(unique_ptrshared_ptr

容器


手动管理容易导致内存泄漏、悬垂指针或敏感数据残留,而 SafeDelete 通过安全释放、内存清零和指针置空,提高了安全性。

深拷贝在涉及大对象时性能较差,而移动语义通过直接转移资源所有权,避免了不必要的复制,提升了效率,尤其适用于临时对象或智能指针(如 std::unique_ptr)。

裸指针需要手动管理生命周期,容易出错,而智能指针(std::unique_ptr 独占所有权、std::shared_ptr 共享所有权)结合容器(如 std::vector)可以自动管理动态对象的生命周期,减少内存泄漏风险,同时利用移动语义优化性能。

相关文章:

  • Linux进程间通信:无名管道与有名管道的原理与实践
  • 4月1日工作日志
  • 用python编写poc的流程
  • 文件系统简介
  • web前端开发-HTML-CSS(0-1)
  • Python入门(4):函数
  • WSN 经典定位算法
  • aerospike6.2.0集群部署
  • python 实现 Celery 任务队列系统
  • LXC 导入(Rockylinux,almalinux,oraclelunx,debian,ubuntu,openEuler,kail,opensuse)
  • 从全球首发到独家量产,远峰科技持续领跑数字钥匙赛道
  • 如何使用cpp操作香橙派GPIO --使用<wiringPi.h>
  • 数据治理的主题库是做什么的
  • pip安装timm依赖失败
  • C++进阶知识复习 1~15
  • Sentinel[超详细讲解]-5
  • 【ROS实战】04-自定义消息并实现ROS服务
  • Java 锁机制详解:用“厕所门”和“防盗门”轻松理解多线程同步
  • delphi intraweb 警告框
  • bluecode-数字增殖问题
  • 证监会披露两起操纵市场处罚结果,今年来涉操纵股票罚没金额超7.5亿元
  • 读懂城市|成都高新区:打造“人尽其才”的“理想之城”
  • 山东茌平民企巨头实控人省外再出手:斥资16亿拿下山西一宗探矿权
  • 一旅客因上错车阻挡车门关闭 ,株洲西高铁站发布通报
  • AI赋能科学红毯,机器人与科学家在虚实之间叩问“科学精神”
  • 受贿1.29亿余元,黑龙江省原副省长王一新被判无期