以下是对 C++ 中相关概念的详细说明及代码示例:
一、动态分配和堆
- 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` 操作符释放之前分配的内存,防止内存泄漏。
- 动态数组:
- 使用
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[]` 操作符释放动态数组的内存。
- 动态对象:
- 对于类对象,使用
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;`:调用对象的析构函数并释放内存。
二、链表
- 链表内的迭代:
- 链表是一种动态数据结构,由节点组成,每个节点包含数据和指向下一个节点的指针。
- 示例:
#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; }`:释放链表节点的内存。
- 递归和列表:
- 可以使用递归方法来处理链表,例如计算链表长度。
- 示例:
#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`。
三、释放内存
-
delete 操作符:
- 用于释放
new
操作符分配的内存,确保在不再使用动态分配的内存时调用。 - 对于对象使用
delete
,对于数组使用delete[]
。 - 示例见上述动态分配和堆部分。
- 用于释放
-
释放内存策略:
- 遵循“谁分配谁释放”原则,避免内存泄漏。在异常处理时也要确保正确释放内存。
- 例如,在函数中分配的内存,在函数结束时或异常抛出时释放:
#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` 确保在异常发生时也能正确释放内存。
- 析构函数:
- 析构函数是类的成员函数,当对象销毁时自动调用,用于释放资源。
- 示例:
#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 类
- charstack.h 接口:
// charstack.h
#ifndef CHARSTACK_H
#define CHARSTACK_Hclass 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
- 选择字符栈的表示:
- 这里选择动态数组表示字符栈,实现
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()` 方法将栈的容量翻倍并复制元素到新的内存中。
在使用这些代码时,需要注意内存的分配和释放,避免出现内存泄漏。同时,对于类的设计,析构函数的正确使用可以确保资源的正确释放,以保证程序的健壮性和内存使用的合理性。