以下是对 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_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
- 选择字符栈的表示:
- 这里选择动态数组表示字符栈,实现
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()` 方法将栈的容量翻倍并复制元素到新的内存中。
在使用这些代码时,需要注意内存的分配和释放,避免出现内存泄漏。同时,对于类的设计,析构函数的正确使用可以确保资源的正确释放,以保证程序的健壮性和内存使用的合理性。