1.c++内存管理
1.代码区
也称Text Segment,存放可执行程序的机器码。
2 数据区:
存放已初始化的全局和静态变量, 常量数据(如字符串常量)。
存放未初始化的全局和静态变量
无疑解释静态变量的来源:
局部静态变量:
存储在静态存储区,函数调用结束后不会销毁,下次调用函数时会保留上一次的值。
3.栈:
从高地址向低地址增长。由编译器自动管理分配。程序中的局部变量、函数参数值、返回变量等存在此区域。
4.堆
从低地址向高地址增长。容量大于栈,程序中动态分配的内存在此区域
2.new和delete问世
2.1对比 malloc new的优势
-
自动调用构造函数和析构函数:
new
在分配内存后会自动调用对象的构造函数进行初始化,delete
在释放内存前会自动调用对象的析构函数进行清理工作,这对于管理复杂对象(如包含动态分配资源的对象)非常方便,能确保资源的正确初始化和释放。
这个无疑是new 能复制对象
-
类型安全:
new
会根据对象的类型自动计算所需的内存大小,返回的指针类型也是正确的,不需要进行强制类型转换,减少了因类型转换错误导致的潜在问题。
malloc返回的是 void* 它得强转
-
操作符重载:在 C++ 中,
new
和delete
是操作符,可以被重载,允许用户自定义内存分配和释放的行为,以满足特定的需求。
操作方便
2.2代码实现
#include <iostream>
using namespace std;
class A {
public:
A() {
std::cout << "A 的构造函数被调用" << std::endl;
}
~A() {
std::cout << "A 的析构函数被调用" << std::endl;
}
};
int main()
{
// 单个变量
int *p= new int;
*p=32;
std::cout << *p << std::endl;
delete p;
//数组
int a[4]={1,23,3,31};//不能赋值
int *ptr=new int[4]{1,2,3};//
cout<<ptr[1]<<endl;
delete [] ptr;
//类
A*ptr_class=new A;
delete ptr_class;
//类数组
A* ptr_classArray=new A[3];
delete [] ptr_classArray;
}
基本格式为 变量类型* ptr = new 变量类型 (数组加[size])
可以重载:
#include <iostream>
#include <cstdlib>
class MyClass {
public:
// 重载全局 new 操作符
static void* operator new(size_t size) {
std::cout << "自定义 new 操作符被调用,分配 " << size << " 字节" << std::endl;
return std::malloc(size);
}
// 重载全局 delete 操作符
static void operator delete(void* ptr) {
std::cout << "自定义 delete 操作符被调用" << std::endl;
std::free(ptr);
}
MyClass() { std::cout << "MyClass 构造函数被调用" << std::endl; }
~MyClass() { std::cout << "MyClass 析构函数被调用" << std::endl; }
};
int main() {
MyClass* obj = new MyClass;
delete obj;
return 0;
}
3.智能指针问世
原因 因为你new 和 delete 是搭配使用的,等你返回,忘记删去怎么办!!!
#include <iostream>
int main()
{
int *ptr=new int[4]{1,2,3,4};
for(int i =0;i<4;i++)
{
if(i=2)
{
return ptr[i];
}
}
delete []ptr;
return 1;
}
3.1 unique_ptr
-
特性
-
独占所有权:
std::unique_ptr
对其所指向的资源拥有独占所有权,同一时间只能有一个std::unique_ptr
指向该资源。这保证了资源管理的清晰性,避免多个指针同时操作同一资源导致的混乱。 -
自动释放:当
std::unique_ptr
对象超出其作用域时,其析构函数会自动调用,从而释放其所指向的资源。
-
代码理解:
#include <iostream>
#include <memory>
using namespace std;
class MyClass {
public:
MyClass() { std::cout << "MyClass 构造函数" << std::endl; }
~MyClass() { std::cout << "MyClass 析构函数" << std::endl; }
};
int main() {
{
// init 第一个利用make_unique<> ()
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
// 当 ptr 离开此作用域时,会自动释放资源
}
{
unique_ptr<MyClass>ptr=make_unique<MyClass>();
//不能赋值unique_ptr<MyClass>ptr1=ptr
unique_ptr<MyClass>ptr1=std::move(ptr);//能右赋值
}
std::cout << "ptr 已释放资源" << std::endl;
return 0;
}
3.2 shared_ptr
特性
-
-
共享所有权:
std::shared_ptr
可以被多个std::shared_ptr
对象共享同一个资源。它使用引用计数机制来跟踪有多少个std::shared_ptr
指向同一资源。 -
自动释放:当引用计数变为 0 时,即没有任何
std::shared_ptr
指向该资源时,资源会被自动释放(还有离开作用域自身减一 一般一对大括号)
-
#include <iostream>
#include <memory>
using namespace std;
class MyClass {
public:
MyClass() { std::cout << "MyClass 构造函数" << std::endl; }
~MyClass() { std::cout << "MyClass 析构函数" << std::endl; }
};
int main() {
/*
{
shared_ptr<MyClass>ptr1=make_shared<MyClass>();
} //error: ‘ptr1’ was not declared in this scope
*/
shared_ptr<MyClass>ptr1=make_shared<MyClass>();
cout<<ptr1.use_count()<<endl;
shared_ptr<MyClass>ptr2=ptr1;//这个拷贝
cout<<ptr2.use_count()<<endl;
shared_ptr<MyClass>ptr3(new MyClass);//这个全新的
cout<<ptr3.use_count()<<endl;
}
/*
MyClass 构造函数
1
2
MyClass 构造函数
1
MyClass 析构函数
MyClass 析构函数
*/ 结果
3.3 weak_ptr
特性
-
弱引用:
std::weak_ptr
是一种不控制资源生命周期的智能指针,它对std::shared_ptr
管理的资源进行弱引用,不增加引用计数。 -
防止循环引用:主要用于解决
std::shared_ptr
可能出现的循环引用问题,避免资源无法正常释放。
错误用法:
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class Boy {
public:
Boy() {
cout << "Boy 构造函数" << endl;
}
~Boy() {
cout << "~Boy 析构函数" << endl;
}
void setGirlFriend(shared_ptr<Girl> _girlFriend) {
this->girlFriend = _girlFriend;
}
private:
shared_ptr<Girl> girlFriend;
};
class Girl {
public:
Girl() {
cout << "Girl 构造函数" << endl;
}
~Girl() {
cout << "~Girl 析构函数" << endl;
}
void setBoyFriend(shared_ptr<Boy> _boyFriend) {
this->boyFriend = _boyFriend;
}
private:
shared_ptr<Boy> boyFriend;
};
void useTrap() {
shared_ptr<Boy> spBoy(new Boy());
shared_ptr<Girl> spGirl(new Girl());
// 陷阱用法
spBoy->setGirlFriend(spGirl);
spGirl->setBoyFriend(spBoy);
// 此时boy和girl的引用计数都是2
}
int main(void) {
useTrap();
system("pause");
return 0;
}
如下图:
当我们执行useTrap函数时,注意,是没有结束此函数,boy和girl指针其实是被两个智能指针托管的,所以他们的引用计数是2
解析:
- 对象创建:
shared_ptr<Boy> spBoy(new Boy());
创建了一个Boy
对象,并使用spBoy
来管理它,此时Boy
对象的引用计数为 1。shared_ptr<Girl> spGirl(new Girl());
创建了一个Girl
对象,并使用spGirl
来管理它,此时Girl
对象的引用计数为 1。- 相互引用:
spBoy->setGirlFriend(spGirl);
使得spBoy
内部的girlFriend
成员(std::shared_ptr<Girl>
类型)指向spGirl
所管理的Girl
对象,Girl
对象的引用计数变为 2。spGirl->setBoyFriend(spBoy);
使得spGirl
内部的boyFriend
成员(std::shared_ptr<Boy>
类型)指向spBoy
所管理的Boy
对象,Boy
对象的引用计数变为 2。2. 函数作用域结束时的情况
当
useTrap
函数执行结束,spBoy
和spGirl
离开作用域,它们会被销毁。按照引用计数的规则,这会使得Boy
和Girl
对象的引用计数各自减 1。然而,由于循环引用的存在:
Boy
对象仍然被spGirl
中的boyFriend
成员引用,所以Boy
对象的引用计数从 2 减为 1。Girl
对象仍然被spBoy
中的girlFriend
成员引用,所以Girl
对象的引用计数从 2 减为 1。因为引用计数没有变为 0,
std::shared_ptr
不会释放Boy
和Girl
对象所占用的内存,从而造成了内存泄漏。
感觉girlfrend和boyfriend反了 才更好理解
真正的:
#include <iostream>
#include <memory>
using namespace std;
class Boy;
class Girl {
public:
Girl() {
cout << "Girl 构造函数" << endl;
}
~Girl() {
cout << "~Girl 析构函数" << endl;
}
void setBoyFriend(shared_ptr<Boy> _boyFriend) {
this->boyFriend = _boyFriend;
}
private:
weak_ptr<Boy> boyFriend;
};
class Boy {
public:
Boy() {
cout << "Boy 构造函数" << endl;
}
~Boy() {
cout << "~Boy 析构函数" << endl;
}
void setGirlFriend(shared_ptr<Girl> _girlFriend) {
this->girlFriend = _girlFriend;
}
private:
weak_ptr<Girl> girlFriend;
};
void useTrap() {
shared_ptr<Boy> spBoy(new Boy());
shared_ptr<Girl> spGirl(new Girl());
spBoy->setGirlFriend(spGirl);
spGirl->setBoyFriend(spBoy);
}
int main() {
useTrap();
return 0;
}
解析:
使用 std::weak_ptr
作为类的成员变量,主要是为了打破 std::shared_ptr
之间可能出现的循环引用问题。当 Boy
对象的 girlFriend
成员使用 std::weak_ptr
时,它对 Girl
对象的引用不会增加 Girl
对象的引用计数,从而避免了循环引用导致的内存泄漏问题。
综上所述,虽然 std::weak_ptr
和 std::shared_ptr
是不同的智能指针类型,但 std::weak_ptr
提供了相应的构造和赋值机制,允许使用 std::shared_ptr
来初始化或赋值,这在解决循环引用问题时非常有用。
其他用法:
#include <iostream>
#include <memory>
// 打印 weak_ptr 相关信息的函数
void printWeakPtrInfo(const std::weak_ptr<int>& weak) {
std::cout << "当前 weak_ptr 的引用计数为: " << weak.use_count() << std::endl;
if (weak.expired()) {
std::cout << "weak_ptr 所指向的对象已被销毁。" << std::endl;
} else {
// 使用 lock 函数获取 shared_ptr
std::shared_ptr<int> shared = weak.lock();
if (shared) {
std::cout << "Value: " << *shared << std::endl;
}
}
}
int main() {
// 创建一个 shared_ptr 并初始化为 42
std::shared_ptr<int> shared = std::make_shared<int>(42);
// 创建一个 weak_ptr 并关联到 shared_ptr
std::weak_ptr<int> weak = shared;
std::cout << "第一次调用 printWeakPtrInfo 函数:" << std::endl;
printWeakPtrInfo(weak);
// 释放 shared_ptr 管理的对象
shared.reset();
std::cout << "\n第二次调用 printWeakPtrInfo 函数:" << std::endl;
printWeakPtrInfo(weak);
return 0;
}