C++之引用与堆内存(new)
通常情况下,编码规范中要求如下:
- 所有的成员变量都要被设置为private
- 对外开放的接口(函数),设置为public
- 不需要对外开放的接口(函数),设置为private或protected
- 如果在类的外部想要访问成员变量,那么可以在类中声明public的函数进行间接访问
- 一般情况下,公有函数声明在类的最开始位置,其后是protected、private函数,最后是成员变量
- 通常同访问权限的函数和变量会分开书写。(增强代码逻辑,更清晰)
- 在类的声明中通常不出现函数定义,函数定义通常都在类的外部实现
- 类声明一般放在.h文件中,类成员函数的定义通常放在.cpp中
一,引用
1.引用的概念:引用是变量的别名。一旦引用和某个变量绑定,就相当于给这个变量起了另一个名字。
使用引用时的注意:
- 引用必须在定义时初始化,不允许空引用
- 引用一旦初始化,就不能再引用其它数据
- 引用实际上就是一块内存的别名
2.引用的主要功能:传递函数的参数 返回函数的值。
3.C++中常用的方式有三种:值传递,指针传递和引用传递
引用传递的性质像指针传递,书写形式像值传递
引用与指针的区别:
- 指针是一个地址,引用是一个别名
- 指针的值可以为空,但是引用的值不能为NULL
- 非const指针可以改变指向,引用不可以
- sizeof引用得到的是变量本身的大小,而指针是4个字节(32位操作系统中)
- 指针和引用在函数传参时也不一样
4.引用的用途
(1)函数参数传递
避免实参拷贝,提高效率,还能在函数中修改实参。
void swap(int& x, int& y) {
int temp = x;
x = y;
y = temp;
}
int main() {
int a = 1, b = 2;
swap(a, b);
cout << "a=" << a << ", b=" << b << endl; // a=2, b=1
}
(2)函数返回值
可以返回引用,让调用者直接操作函数内部的数据
int& getElement(int arr[], int index) {
return arr[index]; // 返回数组元素的引用
}
int main() {
int nums[3] = {10, 20, 30};
getElement(nums, 1) = 200; // 修改第二个元素
cout << nums[1] << endl; // 输出 200
}
(3)常量引用
既能避免拷贝,又能保证函数中不能修改传入参数。常用于传递大对象。
void print(const string& s) {
cout << s << endl;
}
int main() {
string str = "Hello C++";
print(str); // 高效传参,不会拷贝字符串
}
引用示例程序:
#include <iostream>
#include <string>
using namespace std;
// 1. 使用引用作为函数参数
void swapValues(int& x, int& y) {
int temp = x;
x = y;
y = temp;
}
// 2. 使用常量引用
void printMessage(const string& msg) {
cout << "打印信息: " << msg << endl;
}
// 3. 使用引用作为返回值(返回数组元素的引用)
int& getElement(int arr[], int index) {
return arr[index]; // 返回引用,可以修改原数组
}
int main() {
// 引用的基本用法
int a = 10;
int& ref = a; // ref 是 a 的别名
ref = 20; // 改 ref 就是改 a
cout << "1. 引用修改变量: a = " << a << endl;
// 函数参数中的引用
int x = 5, y = 8;
swapValues(x, y);
cout << "2. 交换后: x = " << x << ", y = " << y << endl;
// 常量引用
string str = "Hello C++ 引用";
printMessage(str);
// 返回引用
int arr[3] = {1, 2, 3};
getElement(arr, 1) = 200; // 修改数组的第二个元素
cout << "3. 数组修改后 arr[1] = " << arr[1] << endl;
return 0;
}
运行结果:

二,堆内存管理
什么是堆内存?
栈内存:由编译器自动分配和释放(局部变量、函数参数等),生命周期由作用域决定。
堆内存:程序运行时向操作系统申请的一块内存,由程序员手动管理,直到 delete 或 free 才释放。
全局/静态存储区:存放全局变量和静态变量,由系统在程序运行期间自动管理。
C++ 中堆内存的申请与释放
C++ 提供了两套机制:
(1) C 风格:malloc/free
#include <cstdlib>int* p = (int*)malloc(sizeof(int) * 10); // 申请10个int空间
free(p); // 释放
malloc 返回 void*,需要强制类型转换。
不会调用对象的构造函数 / 析构函数。
示例程序:
#include <iostream>
#include <cstdlib> // malloc, free
using namespace std;
int main() {
// 申请10个 int 空间
int* arr = (int*)malloc(sizeof(int) * 10);
if (!arr) {
cout << "malloc failed!" << endl;
return -1;
}
// 初始化并输出
for (int i = 0; i < 10; i++) {
arr[i] = i * 10;
cout << arr[i] << " ";
}
cout << endl;
// 释放内存
free(arr);
return 0;
}
(2) C++ 风格:new/delete
int* p1 = new int; // 分配一个int
*p1 = 100;delete p1; // 释放
int* p2 = new int[10]; // 分配数组delete[] p2; // 释放数组
new 返回的是指定类型的指针,不需要强转。
会调用对象的构造函数。
delete 会调用析构函数。
注意:数组必须用 delete[] 释放。
示例程序:
#include <iostream>
using namespace std;
int main() {
// 申请单个 int
int* p = new int;
*p = 100;
cout << "单个 int: " << *p << endl;
delete p; // 释放
// 申请 int 数组
int* arr = new int[5];
for (int i = 0; i < 5; i++) {
arr[i] = i + 1;
cout << arr[i] << endl;
}
delete[] arr; //必须用 delete[] 释放
return 0;
}
new/delete不仅可以分配内存,还可以自动调用对象的构造函数和析构函数,因此对于类对象而言,最好使用new/delete来管理
new与delete的区别
相同点:都可以从堆区获取内存
不同点:
1.实现方式
malloc / free:库函数(C 语言风格),本质是函数调用 → 有函数调用开销。
new / delete:C++ 关键字,由编译器直接处理 → 没有函数调用开销。
2.内存大小
malloc:必须手动指定字节大小,例如:
long* p = (long*)malloc(10 * sizeof(long));
new:编译器会根据类型自动计算大小,例如:
long* p = new long[10];
3.对象构造/析构
malloc/free:只分配和释放内存,不会调用对象的构造函数或析构函数。
new/delete:在分配内存时会调用构造函数,在释放内存时会调用析构函数。
4.返回类型
malloc:返回 void*,需要强制类型转换。
new:返回的是对应类型的指针,不需要强转。
5.异常处理
malloc:分配失败时返回 NULL。
new:分配失败时会抛出 std::bad_alloc 异常(除非使用 new(std::nothrow))。
总结:
malloc/free 只是单纯的内存管理函数(C 语言风格)。
new/delete 是 面向对象的内存管理工具,同时管理内存和对象的生命周期。
常见问题:
内存泄漏
申请了堆内存却没有释放。
解决:及时 delete,推荐使用智能指针。
野指针 / 悬空指针
指针未初始化、或内存释放后继续使用。
解决:初始化指针;释放后置为 nullptr。
重复释放
同一块内存 delete 两次,导致程序崩溃。
更多推荐



所有评论(0)