c++面试常见问题:C ++内存管理

1. 内存分区

在 C++ 里,内存主要分为以下几个区域:

  • 栈(Stack):由编译器自动分配和释放,用于存储局部变量、函数参数和返回地址等。其特点是内存分配和释放速度快,遵循后进先出(LIFO)原则。例如:

#include 

void func() {
    int a = 10; // 变量 a 存储在栈上
    std::cout << a << std::endl;
}

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

  • 堆(Heap):由程序员手动管理,通过 new 和 delete(或 malloc 和 free)进行内存的分配和释放。堆的空间较大,但分配和释放的速度相对较慢,且容易出现内存泄漏问题。例如:

#include 

int main() {
    int* ptr = new int(10); // 在堆上分配内存
    std::cout << *ptr << std::endl;
    delete ptr; // 释放堆上的内存
    return 0;
}

  • 全局 / 静态区(Global/Static Area):用于存储全局变量和静态变量。在程序启动时分配内存,程序结束时释放。例如:

#include 

int globalVar = 20; // 全局变量,存储在全局/静态区

void func() {
    static int staticVar = 30; // 静态变量,存储在全局/静态区
    std::cout << staticVar << std::endl;
}

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

  • 常量区(Constant Area):用于存储常量数据,如字符串常量。这些数据在程序运行期间不可修改。例如:

#include 

int main() {
    const char* str = "Hello"; // 字符串常量存储在常量区
    std::cout << str << std::endl;
    return 0;
}

  • 代码区(Code Area):用于存储程序的可执行代码。

2. new 和 delete 与 malloc 和 free 的区别

  • 类型安全性new 和 delete 是 C++ 的运算符,具有类型安全性,会自动计算所需内存的大小,并返回正确类型的指针。而 malloc 和 free 是 C 语言的函数,返回 void* 类型的指针,需要手动进行类型转换。例如:

#include 

int main() {
    int* ptr1 = new int(10); // new 自动处理类型
    int* ptr2 = (int*)malloc(sizeof(int)); // malloc 需要手动类型转换
    delete ptr1;
    free(ptr2);
    return 0;
}

  • 构造和析构new 在分配内存后会调用对象的构造函数进行初始化,delete 在释放内存前会调用对象的析构函数进行资源清理。而 malloc 和 free 只是单纯地分配和释放内存,不会调用构造和析构函数。例如:

#include 

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

int main() {
    MyClass* obj1 = new MyClass(); // 调用构造函数
    delete obj1; // 调用析构函数

    MyClass* obj2 = (MyClass*)malloc(sizeof(MyClass)); // 不调用构造函数
    free(obj2); // 不调用析构函数
    return 0;
}

  • 异常处理new 在内存分配失败时会抛出 std::bad_alloc 异常,而 malloc 在内存分配失败时返回 NULL 指针。

3. 内存泄漏

内存泄漏指的是程序在动态分配内存后,由于某种原因未能正确释放这些内存,导致这部分内存无法被再次使用。常见的原因包括忘记调用 delete 或 free,或者在异常处理中没有正确释放内存。例如:

#include 

void memoryLeak() {
    int* ptr = new int(10);
    // 忘记释放内存
    // delete ptr;
}

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

为避免内存泄漏,可以使用智能指针(如 std::unique_ptrstd::shared_ptr 和 std::weak_ptr),它们会自动管理内存的生命周期。例如:

#include 
#include 

void noMemoryLeak() {
    std::unique_ptr ptr = std::make_unique(10);
    // 不需要手动释放内存,ptr 离开作用域时会自动释放
}

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

4. 悬空指针

悬空指针是指指向已经被释放的内存的指针。使用悬空指针会导致未定义行为。例如:

#include 

int main() {
    int* ptr = new int(10);
    delete ptr;
    // ptr 现在是悬空指针
    // *ptr = 20; // 未定义行为
    return 0;
}

为避免悬空指针问题,在释放内存后将指针置为 nullptr。例如:

#include 

int main() {
    int* ptr = new int(10);
    delete ptr;
    ptr = nullptr; // 将指针置为 nullptr
    return 0;
}

5. 浅拷贝和深拷贝

  • 浅拷贝:浅拷贝只是简单地复制对象的成员变量的值,对于指针成员,只是复制指针的值,而不复制指针所指向的内存。这会导致多个对象共享同一块内存,当其中一个对象释放内存时,其他对象的指针就会成为悬空指针。例如:

收起

cpp

#include 

class MyClass {
public:
    int* data;
    MyClass(int value) {
        data = new int(value);
    }
    // 浅拷贝构造函数
    MyClass(const MyClass& other) {
        data = other.data;
    }
    ~MyClass() {
        delete data;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(obj1); // 浅拷贝
    // 这里会出现问题,因为 obj1 和 obj2 共享同一块内存
    return 0;
}

  • 深拷贝:深拷贝会为新对象的指针成员分配新的内存,并将原对象指针所指向的内存内容复制到新的内存中。这样每个对象都有自己独立的内存副本,避免了悬空指针问题。例如:

收起

cpp

#include 

class MyClass {
public:
    int* data;
    MyClass(int value) {
        data = new int(value);
    }
    // 深拷贝构造函数
    MyClass(const MyClass& other) {
        data = new int(*other.data);
    }
    ~MyClass() {
        delete data;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(obj1); // 深拷贝
    return 0;
}

6. 智能指针

智能指针是 C++ 标准库提供的模板类,用于自动管理动态分配的内存,避免手动管理内存带来的问题。常见的智能指针有:

  • std::unique_ptr:独占所有权的智能指针,同一时间只能有一个 std::unique_ptr 指向某个对象。例如:

收起

cpp

#include 
#include 

int main() {
    std::unique_ptr ptr = std::make_unique(10);
    // std::unique_ptr ptr2 = ptr; // 错误,不能复制
    std::unique_ptr ptr2 = std::move(ptr); // 可以转移所有权
    return 0;
}

  • std::shared_ptr:共享所有权的智能指针,多个 std::shared_ptr 可以指向同一个对象,使用引用计数来管理对象的生命周期,当引用计数为 0 时,对象会被自动释放。例如:

收起

cpp

#include 
#include 

int main() {
    std::shared_ptr ptr1 = std::make_shared(10);
    std::shared_ptr ptr2 = ptr1; // 可以复制
    return 0;
}
  • std::weak_ptr:弱引用的智能指针,它不拥有对象的所有权,主要用于解决 std::shared_ptr 的循环引用问题。例如:
#include 
#include 

class B;

class A {
public:
    std::shared_ptr bPtr;
    ~A() { std::cout << "A destructor" << std::endl; }
};

class B {
public:
    std::weak_ptr aPtr; // 使用 std::weak_ptr 避免循环引用
    ~B() { std::cout << "B destructor" << std::endl; }
};

int main() {
    std::shared_ptr a = std::make_shared();
    std::shared_ptr b = std::make_shared();
    a->bPtr = b;
    b->aPtr = a;
    return 0;
}

你可能感兴趣的:(c++,算法,开发语言)