【原始指针(*、malloc、new)和智能指针(shared_ptr、unique_ptr)的区别、理解和使用】

目录

 

一、什么是原始指针(*、&)、什么是动态分配和释放内存(malloc和free、new和delete)

      1.1无动态分配内存的使用方法

      1.2动态分配内存使用方法

      1.3为什么要动态分配内存?

二、智能指针(shared_ptr、unique_ptr)和原始指针区别、如何使用

       2.1智能指针(shared_ptr、unique_ptr)和原始指针(*)区别

       2.2shared_ptr、unique_ptr的区别

       2.3智能指针一定比原始指针好吗


 

一、什么是原始指针(*、&)、什么是动态分配和释放内存(malloc和free、new和delete)

        原始指针是指直接操作内存地址的指针,通常用 * 和 & 运算符来读取和赋值内存地址。它们是 C 和 C++ 中的基本概念,用于进行低级别的内存管理和操作。

      1.1无动态分配内存的使用方法

        在函数内部定义的指针 MyClass* ptr; 如果没有使用 new或malloc 来创建对象,而是指向了一个栈上的对象或者是全局对象,那么当函数结束时,这个指针所指向的对象会自动被销毁,无需手动释放。示例代码如下:

void nonDynamicPointerExample() {
    MyClass obj;  // 在栈上创建对象
    MyClass* ptr = &obj;  // 指针指向栈上的对象,不需要手动释放
}
int main() {
    nonDynamicPointerExample();  // 调用示例函数
    return 0;
}

      1.2动态分配内存使用方法

        new 和 delete 是 C++ 中的关键字,用于动态分配和释放内存。它们可以调用对象的构造函数和析构函数,提供了更高级的内存管理功能。与之相对应的是 C 语言中的 malloc 和 free 函数,用于动态分配和释放内存。

int* ptr = new int;  // 使用 new 分配整数的内存空间
*ptr = 42;  // 在分配的内存中存储数据
delete ptr;  // 使用 delete 释放内存

int* ptr = (int*)malloc(sizeof(int));  // 使用 malloc 分配整数的内存空间
*ptr = 42;  // 在分配的内存中存储数据
free(ptr);  // 使用 free 释放内存

      1.3为什么要动态分配内存?

        在函数内部使用 new 动态分配的局部变量相对于直接创建栈上的局部变量(自动变量)有一些特定的优点和用途:

        a.生命周期控制:通过 new 创建的局部变量的生命周期不再局限于函数的执行范围。它们可以在函数结束后继续存在,直到显式调用 delete 进行释放为止。这使得你可以在函数外部继续使用这些对象,或者将它们传递给其他函数,而不会导致对象的销毁。

        b.内存空间灵活性:使用 new 创建的局部变量可以分配比栈上局部变量更大的内存空间,因为栈空间通常是有限的。这对于需要大量内存或者动态增长的数据结构(如动态数组)非常有用。

        c.对象持久性:有时候需要在函数调用之间保持对象的状态或者数据。通过 new 创建的局部变量可以在函数调用之间保持对象的状态,而不会因为函数结束而销毁。

        下 面是一个示例,展示了如何在函数内部使用 new 创建对象,并将对象的指针传递到函数外部进行使用:

#include <iostream>

class MyClass {
public:
    MyClass(int val) : value(val) {}
    void printValue() { std::cout << "Value: " << value << std::endl; }

private:
    int value;
};

MyClass* createObject() {
    MyClass* ptr = new MyClass(42);
    return ptr;
}

int main() {
    MyClass* objPtr = createObject();  // 在函数内部创建对象,并返回指针
    objPtr->printValue();  // 在函数外部使用对象指针调用成员函数

    delete objPtr;  // 释放内存

    return 0;
}

        在这个示例中,createObject 函数内部使用 new 创建了一个 MyClass 对象,并返回了这个对象的指针给调用函数 main。调用函数 main 可以通过这个指针访问并操作这个对象,直到需要释放内存时调用 delete。

  1. malloc 和 new有什么区别

        malloc 和 new 是用于在 C 和 C++ 中动态分配内存的关键字,它们之间有几个重要的区别:

        a.用法和语法:

                - malloc 是 C 语言中的函数,用于动态分配内存。它的原型是 void* malloc(size_t size),返回一个指向分配内存的指针。

                 - new 是 C++ 中的关键字,用于动态分配内存并构造对象。它有多种形式,例如 new、new[]、new (placement) 等,用于不同的内存分配和构造对象的场景。

        b.类型安全:

                 - malloc 返回的是 void* 类型的指针,需要进行类型转换后才能使用,容易出现类型错误。

                - new 在分配内存时可以直接指定所需对象的类型,它会自动进行类型转换并返回相应类型的指针,因此更加类型安全。

        c.构造函数调用: - malloc 分配的内存只是一块原始的内存空间,并不会调用对象的构造函数。如果需要构造对象,需要手动调用构造函数。 - new 在分配内存后会自动调用对象的构造函数来初始化对象,因此更方便且符合面向对象的设计理念。

         d.内存管理:

                 - malloc 分配的内存需要手动调用 free 函数来释放,否则可能会造成内存泄漏。

                 - new 分配的内存会在对象生命周期结束时自动调用对象的析构函数,并释放相关内存,不需要手动释放。在 C++ 中,当对象的生命周期结束时(例如对象超出作用域、delete 对象指针等),会自动调用对象的析构函数。但这并不是由 new 自动调用析构函数并释放内存,而是由 C++ 的对象生命周期管理机制来实现的。

         e.数组分配:

                - malloc 和 free 通常用于分配和释放动态数组的内存。

                - new[] 和 delete[] 是 C++ 中专门用于分配和释放动态数组的关键字,它们可以正确处理数组元素的构造和析构。

        总的来说,new 更适用于 C++ 中的对象管理和构造,提供了更多的便利和类型安全;而 malloc 则更适合于 C 语言中的内存分配和释放,不涉及对象的构造和析构。在 C++ 中推荐使用 new 和 delete,在 C 语言中使用 malloc 和 free。

二、智能指针(shared_ptr、unique_ptr)和原始指针区别、如何使用

       2.1智能指针(shared_ptr、unique_ptr)和原始指针(*)区别

        a.资源管理:

                - 非智能指针:例如裸指针 T* ptr = new T;,需要手动管理资源的生命周期,包括手动释放内存(delete ptr;)和处理异常情况等。如果忘记释放内存或者处理异常,可能会导致内存泄漏或者程序崩溃。

                - 智能指针:例如 std::shared_ptr<T> ptr = std::make_shared<T>();,智能指针会自动管理资源的生命周期,当没有指针指向资源时会自动释放资源,避免了内存泄漏和手动管理资源的繁琐工作。

        b.所有权管理:

                - 非智能指针:所有权需要手动管理,如果多个地方都需要访问同一个资源,需要自行确保正确地传递和释放所有权。

                - 智能指针:智能指针可以自动管理所有权,例如 std::shared_ptr 允许多个指针共享同一个资源,通过引用计数来管理所有权,而 std::unique_ptr 则是独占所有权的智能指针。 

         下面是一个简单的实例,展示了智能指针和非智能指针的区别:

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructor\n"; }
    ~MyClass() { std::cout << "MyClass destructor\n"; }
};

void nonSmartPointerExample() {
    MyClass* ptr = new MyClass();  // 非智能指针,需要手动释放资源
    delete ptr;  // 手动释放内存
}

void smartPointerExample() {
    std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();  // 智能指针,自动管理资源生命周期
}
void smartPointerExample2() {
    std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(42);  // 智能指针,自动管理资源生命周期
}

int main() {
    nonSmartPointerExample();  // 非智能指针的例子
    smartPointerExample();  // 智能指针的例子
    smartPointerExample2(); // 智能指针的例子
    return 0;
}

        在上面的例子中,nonSmartPointerExample 使用了非智能指针 MyClass* ptr,需要手动释放内存;而smartPointerExample 使用了智能指针 std::unique_ptr,资源的管理由智能指针自动完成,不需要手动释放。

       2.2shared_ptr、unique_ptr的区别

        std::shared_ptr 和 std::unique_ptr 是 C++ 中用于管理动态分配的资源(如内存)的智能指针。它们之间的区别主要体现在所有权和生命周期管理上:

        a.std::shared_ptr:

                - 允许多个指针共享同一个对象,通过引用计数来管理对象的生命周期。

                 - 当最后一个指向对象的 shared_ptr 被销毁时,对象才会被销毁,从而可以实现多个地方共享同一个资源。

                - 可以通过 std::make_shared 或 std::shared_ptr 的构造函数来创建。 

       b.std::unique_ptr:

                - 独占所有权,不能拷贝或者赋值给其他 unique_ptr。

                - 当指向对象的 unique_ptr 被销毁时,对象也会被销毁,可以确保资源的独占性和生命周期管理。

                - 适合于单一所有权的情况,比如只有一个地方需要拥有和管理资源。 

         综上所述,区别在于对资源所有权和生命周期管理的不同需求。std::shared_ptr 适用于多个地方共享同一个资源的情况,而 std::unique_ptr 适用于单一所有权的情况。 当使用std::unique_ptr 时,需要注意其独占所有权的特性。

        下面是一个使用 std::unique_ptr 的实际代码示例:

#include <iostream>
#include <memory>  // 包含智能指针头文件

class MyClass {
public:
    MyClass(int val) : value(val) {}
    void printValue() { std::cout << "Value: " << value << std::endl; }

private:
    int value;
};

std::unique_ptr<MyClass> createObject() {
    // 使用std::make_unique创建unique_ptr
    std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>(42);
    return ptr;
}

int main() {
    std::unique_ptr<MyClass> objPtr1 = createObject();  // 在函数内部创建unique_ptr对象
    objPtr1->printValue();  // 在函数外部使用对象指针调用成员函数
    std::unique_ptr<MyClass> objPtr2 = std::move(objPtr1);  // 转移资源所有权
    

    // unique_ptr在函数结束时会自动释放所管理的对象,无需手动调用delete

    return 0;
}

        在这个示例中,createObject 函数返回一个 std::unique_ptr,在函数结束时,objPtr 1的生命周期结束,指向的对象会自动释放。与 std::shared_ptr 不同,std::unique_ptr 确保了独占所有权,同一时间只有一个指针可以指向所管理的对象。需要注意的是,std::unique_ptr 不能直接进行复制操作,因为它禁止了复制构造函数和赋值操作符。如果需要转移资源所有权,可以使用 std::move 来进行移动语义的操作objPtr 2。

       2.3智能指针一定比原始指针好吗

        当谈及智能指针(例如 std::shared_ptr、std::unique_ptr)和原始指针(如 malloc、new)时,它们之间有几个关键区别以及各自的优缺点和适用场景:

        a.智能指针:

        - 优点:

                 - 自动内存管理:智能指针可以自动管理内存,避免了手动释放内存的问题。

                 - 安全性增强:智能指针提供了类型安全,避免了常见的指针错误。

                 - 生命周期管理:智能指针在作用域结束时自动释放内存,避免了手动内存管理带来的问题。

        - 缺点:

                 - 额外开销:智能指针可能会有一些额外开销,因为它们管理了额外的元数据。

                 - 不适用于特定情况:有些情况下,使用智能指针可能不太合适,比如需要对对象进行底层内存管理、需要与 C 接口交互、需要实现自定义的内存管理策略等。

         - 适用场景:

         - std::shared_ptr:用于共享所有权的情况,允许多个指针共同指向同一对象。

         - std::unique_ptr:用于独占所有权的情况,确保只有一个指针拥有对象的所有权。

        a.原始指针:

         - 优点:

                 - 灵活性:原始指针提供了更多的灵活性,可以用于更底层的内存管理。

                 - 不带开销:原始指针没有智能指针的额外开销。

        - 缺点:

                - 容易出错:容易出现内存泄漏、悬挂指针和释放非法指针等问题。

                - 手动管理:需要手动释放内存,容易导致内存泄漏或内存安全问题。

         - 适用场景:

                - 对于低级别的内存管理、与 C 语言接口交互或需要更高的性能要求时。

        总的来说,智能指针提供了更安全和方便的内存管理方式,尤其适用于管理对象的所有权和生命周期。而原始指针则更适合于一些特定的、需要更灵活内存管理的场景。

 

要创建一个返回 `std::shared_ptr<std::unordered_map<std::string, std::string>>` 的函数,你需要在 C++使用 `std::make_shared` 函数,并将其与所需的 unordered_map 类型结合。以下是一个示例函数,展示了如何完成这个任务: ```cpp #include <iostream> #include <unordered_map> #include <memory> // 定义一个函数,它返回一个指向 std::unordered_map<std::string, std::string> 的 std::shared_ptr std::shared_ptr<std::unordered_map<std::string, std::string>> createMap() { // 创建并初始化一个 unordered_map 对象 std::unordered_map<std::string, std::string> map{ {"key1", "value1"}, {"key2", "value2"} }; // 使用 std::make_shared 来生成一个指向 map 的 shared_ptr return std::make_shared<std::unordered_map<std::string, std::string>>(map); } int main() { // 调用函数,并存储结果到变量 auto result = createMap(); // 检查结果是否为空引用 if (result != nullptr) { // 打印 map 内容 for (const auto &pair : *result) { std::cout << pair.first << ": " << pair.second << std::endl; } } else { std::cout << "Failed to create the map." << std::endl; } return 0; } ``` ### 相关问题: 1. **如何在函数中管理动态内存?** 当需要在函数内部分配动态内存时,可以使用 `new` 或 `malloc()` 等操作,但在现代 C++ 中通常推荐使用智能指针如 `std::unique_ptr` 或 `std::shared_ptr` 来自动处理内存管理。 2. **如何确保函数安全地返回资源?** 为了确保函数返回的对象不会导致资源泄漏,应使用合适的构造函数或析构函数,例如在 `std::make_unique` 和 `std::make_shared` 中,它们会妥善地管理新分配的资源。 3. **在何时应该选择使用 `std::shared_ptr` 而不是其他容器?** `std::shared_ptr` 应该用于共享资源,当多个对象都希望拥有对资源的所有权并且在任意时刻都不应该直接销毁对象而可能导致数据丢失的情况下。例如,在复杂数字图形界面库中,或者在需要跟踪多个依赖项的对象生命周期的场景下。对于简单的、一次性使用的资源,考虑使用 `std::unique_ptr` 可能更为合适。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值