C++入门-内存管理

大家好啊,好久不见了,今天来跟大家聊聊C++中的内存管理,坐稳咯,开始发车!

1. C/C++内存分布

在C/C++程序中,内存通常被划分为四个区域:栈、堆、全局/静态存储区和常量存储区。每个区域都有其特定的作用和生命周期。

栈(Stack)

栈用于存储函数的局部变量、函数参数和函数调用信息。栈上的内存分配和释放是自动进行的,由编译器负责管理。当函数调用结束时,栈上的局部变量会被自动销毁。

#include <iostream>

void func() {
    int x = 10; // x是栈上的局部变量
    std::cout << "x: " << x << std::endl;
}

int main() {
    func();
    return 0;
}

堆(Heap)

堆用于动态分配内存,大小不固定,可以在运行时动态分配和释放内存。堆上的内存需要我们手动管理,通过newdelete运算符来分配和释放内存。

#include <iostream>

int main() {
    // 在堆上动态分配一个整数
    int* ptr = new int(5);
    
    std::cout << "Value at ptr: " << *ptr << std::endl;
    
    // 释放堆上的内存
    delete ptr;
    
    return 0;
}

2. C语言中的动态内存管理方式

在C语言中,动态内存管理主要通过mallocfree函数来实现。malloc函数用于动态分配内存,返回一个指向分配内存的指针,free函数用于释放动态分配的内存。

示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    // 动态分配一个整型变量
    int* ptr = (int*)malloc(sizeof(int));
    
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    
    *ptr = 10;
    printf("Value at ptr: %d\n", *ptr);
    
    // 释放动态分配的内存
    free(ptr);
    
    return 0;
}

在C语言中,我们需要手动管理动态分配的内存,确保在不再需要使用内存时及时释放,以避免内存泄漏问题。

3. C++中的动态内存管理

在C++中,动态内存管理更加方便和灵活,主要通过newdelete运算符来实现。new用于动态分配内存并调用对象的构造函数,返回指向分配内存的指针;delete用于释放动态分配的内存并调用对象的析构函数。

示例代码:

#include <iostream>

class MyClass {
public:
    MyClass() {
        std::cout << "Constructor called" << std::endl;
    }
    
    ~MyClass() {
        std::cout << "Destructor called" << std::endl;
    }
};

int main() {
    // 动态分配一个MyClass对象
    MyClass* obj = new MyClass();
    
    // 使用动态分配的对象
    // ...
    
    // 释放动态分配的对象
    delete obj;
    
    return 0;
}

在C++中,使用newdelete进行动态内存管理时,我们无需手动计算内存大小,同时也不需要手动调用对象的构造函数和析构函数,这些由编译器和运行时系统自动处理。

4. operator new与operator delete函数

在C++中,operator newoperator delete是用于动态内存分配和释放的全局函数。它们可以被重载以实现自定义的内存分配和释放行为。

operator new函数

operator new函数用于动态分配内存,其原型如下:

void* operator new(std::size_t size);

当使用new运算符分配内存时,实际上会调用operator new函数来执行内存分配操作。我们可以重载operator new函数,以实现特定的内存分配策略。

operator delete函数

operator delete函数用于释放动态分配的内存,其原型如下:

void operator delete(void* ptr) noexcept;

当使用delete运算符释放内存时,实际上会调用operator delete函数来执行内存释放操作。我们可以重载operator delete函数,以实现特定的内存释放策略。

示例代码:

#include <iostream>
#include <cstdlib>

void* operator new(std::size_t size) {
    std::cout << "Custom operator new called. Size: " << size << std::endl;
    return std::malloc(size);
}

void operator delete(void* ptr) noexcept {
    std::cout << "Custom operator delete called" << std::endl;
    std::free(ptr);
}

int main() {
    int* ptr = new int;
    delete ptr;
    
    return 0;
}

在上面的示例中,我们重载了operator newoperator delete函数,输出了自定义的内存分配和释放信息。通过重载这两个函数,我们可以实现自定义的内存管理行为。

5. new和delete的实现原理

在C++中,newdelete是用于动态内存分配和释放的关键操作符。它们的实现原理对于理解内存管理机制至关重要。

new的实现原理

当使用new运算符来动态分配内存时,实际上会经过以下步骤:

  1. 调用operator new函数来分配内存空间。
  2. 调用对象的构造函数来初始化对象。
  3. 返回指向新分配对象的指针。

下面是一个简单的示例代码,演示了new的实现原理:

#include <iostream>

class MyClass {
public:
    MyClass() {
        std::cout << "Constructor called" << std::endl;
    }
};

int main() {
    MyClass* obj = new MyClass();
    
    delete obj;
    
    return 0;
}

在上面的示例中,new运算符会调用operator new函数分配内存,并调用MyClass的构造函数来初始化对象。

delete的实现原理

当使用delete运算符释放动态分配的内存时,实际上会经过以下步骤:

  1. 调用对象的析构函数来清理对象。
  2. 调用operator delete函数来释放内存空间。

下面是一个简单的示例代码,演示了delete的实现原理:

#include <iostream>

class MyClass {
public:
    ~MyClass() {
        std::cout << "Destructor called" << std::endl;
    }
};

int main() {
    MyClass* obj = new MyClass();
    
    delete obj;
    
    return 0;
}

在上面的示例中,delete运算符会先调用MyClass的析构函数,然后调用operator delete函数释放内存。

6. 定位new表达式(placement-new)

在C++中,定位new表达式(placement-new)是一种特殊形式的内存分配方式,允许我们在已分配的内存地址上构造对象。这在某些情况下非常有用,例如在特定的内存区域上构造对象,或者在内存池中管理对象。

定位new表达式的语法

定位new表达式的语法如下:

new (pointer) Type[initializer];

其中,pointer是指向已分配内存的指针,Type是要构造的对象类型,initializer是可选的初始化参数。

定位new表达式的示例

下面是一个简单的示例代码,演示了定位new表达式的用法:

#include <iostream>

class MyClass {
public:
    MyClass(int value) : m_value(value) {
        std::cout << "Constructor called with value " << m_value << std::endl;
    }

    void printValue() {
        std::cout << "Value: " << m_value << std::endl;
    }

private:
    int m_value;
};

int main() {
    // 分配内存
    char buffer[sizeof(MyClass)];
    
    // 在已分配的内存地址上构造对象
    MyClass* obj = new (buffer) MyClass(42);

    // 调用对象的成员函数
    obj->printValue();

    // 显式调用对象的析构函数
    obj->~MyClass();

    return 0;
}

在上面的示例中,我们首先分配了足够大小的内存空间,然后使用定位new表达式在这块内存地址上构造了一个MyClass对象。最后,我们显式调用了对象的析构函数来手动释放资源。

定位new表达式在特定的内存管理场景中非常有用,可以精确地控制对象的构造和析构过程。

7. 常见面试题

在面试中,关于C++内存管理的问题经常会被问及。下面列举了一些常见的面试题,以及它们的解答和相应的代码示例。

面试题1:什么是浅拷贝和深拷贝?如何避免浅拷贝带来的问题?

解答: 浅拷贝是简单地复制对象的值,包括指针成员的地址。深拷贝是复制对象的所有内容,包括指针指向的内容。为避免浅拷贝带来的问题,需要实现自定义的拷贝构造函数和赋值运算符重载函数,确保正确地复制对象的内容。

#include <iostream>
#include <cstring>

class MyString {
public:
    char* m_data;

    MyString(const char* data) {
        m_data = new char[strlen(data) + 1];
        strcpy(m_data, data);
    }

    // 拷贝构造函数
    MyString(const MyString& other) {
        m_data = new char[strlen(other.m_data) + 1];
        strcpy(m_data, other.m_data);
    }

    // 赋值运算符重载
    MyString& operator=(const MyString& other) {
        if (this != &other) {
            delete[] m_data;
            m_data = new char[strlen(other.m_data) + 1];
            strcpy(m_data, other.m_data);
        }
        return *this;
    }

    ~MyString() {
        delete[] m_data;
    }
};

int main() {
    MyString str1("Hello");
    MyString str2 = str1; // 调用拷贝构造函数
    MyString str3("World");
    str3 = str1; // 调用赋值运算符重载

    return 0;
}

面试题2:什么是智能指针?它们的作用是什么?举例说明。

解答: 智能指针是一种类似指针的对象,它管理动态分配的内存资源,自动进行内存的分配和释放,避免内存泄漏等问题。常见的智能指针有std::unique_ptrstd::shared_ptrstd::unique_ptr拥有独占的所有权,而std::shared_ptr可以共享所有权。

#include <iostream>
#include <memory>

int main() {
    // 使用std::unique_ptr
    std::unique_ptr<int> ptr(new int(42));
    std::cout << *ptr << std::endl;

    // 使用std::shared_ptr
    std::shared_ptr<int> ptr2 = std::make_shared<int>(100);
    std::cout << *ptr2 << std::endl;

    return 0;
}

面试题3:什么是内存泄漏?如何避免内存泄漏?

解答: 内存泄漏是指程序中动态分配的内存未被正确释放,导致系统中存在无法访问的内存块。为避免内存泄漏,应该始终在动态分配内存后及时释放,可以使用智能指针、RAII等技术来管理动态内存。

好了,感谢大家能看到这,如果这篇文章对你有帮助的话,还请点个赞支持一下,有什么问题也可以评论区留言,那么我们下次再见啦,Peace~
在这里插入图片描述

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值