C++---------动态内存管理

发布于:2025-02-11 ⋅ 阅读:(52) ⋅ 点赞:(0)

以下是对 C++ 中相关概念的详细说明及代码示例:

一、动态分配和堆

  1. new 操作符
    • new 操作符用于在堆上动态分配内存。它会调用对象的构造函数(如果是类对象)并返回指向分配内存的指针。
    • 示例:
#include <iostream>
using namespace std;

int main() {
    // 为一个整数分配内存
    int* p = new int;
    *p = 10;
    cout << *p << endl;
    // 释放内存
    delete p;
    return 0;
}
- 代码解释:
    - `int* p = new int;`:使用 `new` 操作符在堆上分配了足够存储一个 `int` 的内存空间,并将该内存地址存储在指针 `p` 中。
    - `*p = 10;`:将值 `10` 存储到 `p` 所指向的内存中。
    - `delete p;`:使用 `delete` 操作符释放之前分配的内存,防止内存泄漏。
  1. 动态数组
    • 使用 new 操作符可以创建动态数组,需要使用 delete[] 来释放。
    • 示例:
#include <iostream>
using namespace std;

int main() {
    int size = 5;
    // 为包含 5 个整数的数组分配内存
    int* arr = new int[size];
    for (int i = 0; i < size; ++i) {
        arr[i] = i * 2;
    }
    for (int i = 0; i < size; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
    // 释放动态数组内存
    delete[] arr;
    return 0;
}
- 代码解释:
    - `int* arr = new int[size];`:在堆上分配了一个包含 `size` 个 `int` 元素的数组。
    - `for (int i = 0; i < size; ++i) { arr[i] = i * 2; }`:为数组元素赋值。
    - `delete[] arr;`:使用 `delete[]` 操作符释放动态数组的内存。

在这里插入图片描述

  1. 动态对象
    • 对于类对象,使用 new 操作符可以动态创建对象,同时会调用对象的构造函数。
    • 示例:
#include <iostream>
using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "MyClass object is created." << endl;
    }
    ~MyClass() {
        cout << "MyClass object is destroyed." << endl;
    }
};

int main() {
    // 动态创建 MyClass 对象
    MyClass* obj = new MyClass();
    // 释放对象内存
    delete obj;
    return 0;
}
- 代码解释:
    - `MyClass* obj = new MyClass();`:在堆上创建 `MyClass` 的对象,调用其构造函数。
    - `delete obj;`:调用对象的析构函数并释放内存。

二、链表

  1. 链表内的迭代
    • 链表是一种动态数据结构,由节点组成,每个节点包含数据和指向下一个节点的指针。
    • 示例:
#include <iostream>
using namespace std;

struct Node {
    int data;
    Node* next;
    Node(int d) : data(d), next(nullptr) {}
};

int main() {
    Node* head = new Node(1);
    Node* second = new Node(2);
    Node* third = new Node(3);
    head->next = second;
    second->next = third;

    // 迭代链表
    Node* curr = head;
    while (curr!= nullptr) {
        cout << curr->data << " ";
        curr = curr->next;
    }
    cout << endl;

    // 释放链表内存
    curr = head;
    while (curr!= nullptr) {
        Node* temp = curr;
        curr = curr->next;
        delete temp;
    }
    return 0;
}
- 代码解释:
    - `Node` 结构体定义了链表的节点,包含 `data` 和 `next` 指针。
    - `head->next = second;` 和 `second->next = third;` 建立了链表的链接。
    - `while (curr!= nullptr) { cout << curr->data << " "; curr = curr->next; }`:通过迭代遍历链表并输出数据。
    - `while (curr!= nullptr) { Node* temp = curr; curr = curr->next; delete temp; }`:释放链表节点的内存。
  1. 递归和列表
    • 可以使用递归方法来处理链表,例如计算链表长度。
    • 示例:
#include <iostream>
using namespace std;

struct Node {
    int data;
    Node* next;
    Node(int d) : data(d), next(nullptr) {}
};

// 递归计算链表长度
int length(Node* head) {
    if (head == nullptr) return 0;
    return 1 + length(head->next);
}

int main() {
    Node* head = new Node(1);
    Node* second = new Node(2);
    Node* third = new Node(3);
    head->next = second;
    second->next = third;
    cout << "Length of the list: " << length(head) << endl;

    // 释放链表内存
    Node* curr = head;
    while (curr!= nullptr) {
        Node* temp = curr;
        curr = curr->next;
        delete temp;
    }
    return 0;
}
- 代码解释:
    - `length(Node* head)` 函数使用递归方法计算链表的长度,通过不断递归调用 `length(head->next)` 直到 `head` 为 `nullptr`。

三、释放内存

  1. delete 操作符

    • 用于释放 new 操作符分配的内存,确保在不再使用动态分配的内存时调用。
    • 对于对象使用 delete,对于数组使用 delete[]
    • 示例见上述动态分配和堆部分。
  2. 释放内存策略

    • 遵循“谁分配谁释放”原则,避免内存泄漏。在异常处理时也要确保正确释放内存。
    • 例如,在函数中分配的内存,在函数结束时或异常抛出时释放:
#include <iostream>
#include <stdexcept>
using namespace std;

void func() {
    int* p = new int;
    try {
        // 一些操作
        *p = 10;
        if (*p == 10) {
            throw runtime_error("Error occurred");
        }
        delete p;
    } catch (const exception& e) {
        delete p;
        throw;
    }
}

int main() {
    try {
        func();
    } catch (const exception& e) {
        cout << e.what() << endl;
    }
    return 0;
}
- 代码解释:
    - 在 `func()` 函数中,首先分配内存,在操作过程中可能抛出异常,使用 `try-catch` 确保在异常发生时也能正确释放内存。
  1. 析构函数
    • 析构函数是类的成员函数,当对象销毁时自动调用,用于释放资源。
    • 示例:
#include <iostream>
using namespace std;

class MyResource {
public:
    MyResource() {
        cout << "Resource acquired." << endl;
    }
    ~MyResource() {
        cout << "Resource released." << endl;
    }
};

class MyClass {
private:
    MyResource* res;
public:
    MyClass() {
        res = new MyResource();
    }
    ~MyClass() {
        delete res;
    }
};

int main() {
    MyClass obj;
    // 当 main 函数结束,obj 的析构函数会被调用,释放资源
    return 0;
}
- 代码解释:
    - `MyClass` 的构造函数中创建 `MyResource` 对象,析构函数中释放该对象,确保资源的释放。

四、定义 charstack 类

  1. charstack.h 接口
// charstack.h
#ifndef CHARSTACK_H
#define CHARSTACK_H

class CharStack {
private:
    char* stack;
    int top;
    int capacity;
    void resize();
public:
    CharStack();
    ~CharStack();
    void push(char c);
    char pop();
    bool isEmpty() const;
    bool isFull() const;
};

#endif
  1. 选择字符栈的表示
    • 这里选择动态数组表示字符栈,实现 charstack.cpp 如下:
// charstack.cpp
#include "charstack.h"
#include <iostream>

CharStack::CharStack() : top(-1), capacity(10) {
    stack = new char[capacity];
}

CharStack::~CharStack() {
    delete[] stack;
}

void CharStack::push(char c) {
    if (isFull()) {
        resize();
    }
    stack[++top] = c;
}

char CharStack::pop() {
    if (isEmpty()) {
        throw out_of_range("Stack is empty.");
    }
    return stack[top--];
}

bool CharStack::isEmpty() const {
    return top == -1;
}

bool CharStack::isFull() const {
    return top == capacity - 1;
}

void CharStack::resize() {
    capacity = capacity * 2;
    char* newStack = new char[capacity];
    for (int i = 0; i <= top; ++i) {
        newStack[i] = stack[i];
    }
    delete[] stack;
    stack = newStack;
}
- 代码解释:
    - `CharStack` 类使用动态数组存储字符元素。
    - `push(char c)` 方法将元素入栈,如果栈满调用 `resize()` 方法扩容。
    - `pop()` 方法将元素出栈,如果栈空抛出异常。
    - `resize()` 方法将栈的容量翻倍并复制元素到新的内存中。

在使用这些代码时,需要注意内存的分配和释放,避免出现内存泄漏。同时,对于类的设计,析构函数的正确使用可以确保资源的正确释放,以保证程序的健壮性和内存使用的合理性。
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到