智能指针用法学习

转自:https://blog.csdn.net/cpp_learner/article/details/118912592,chatgpt

1.介绍

智能指针就是帮管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏!使用智能指针可以自动调用对象的析构函数。

 例子:

class Test {
public:
    Test() { cout << "Test的构造函数..." << endl; }
    ~Test() { cout << "Test的析构函数..." << endl; }

    int getDebug() { return this->debug; }

private:
    int debug = 20;
};

int main() { 
    //Test *test = new Test;
    shared_ptr<Test> test(new Test);

    cout << "test->debug:" << test->getDebug() << endl;
    cout << "(*test).debug:" << (*test).getDebug() << endl;
    return 0;
}
//输出结果
Test的构造函数...
test->debug:20
(*test).debug:20
Test的析构函数... 

智能指针可以像普通指针一样使用-> 和*取内容,是因为 shared_ptr重载了这2个操作符函数:

1.1 get方法

获取智能指针托管的指针地址。但一般不这样用,直接用智能指针操作即可。

// 定义智能指针
shared_ptr<Test> test(new Test);

Test *tmp = test.get();        // 获取指针返回
cout << "tmp->debug:" << tmp->getDebug() << endl;

// 函数原型
_NODISCARD _Ty * get() const noexcept
{    // return wrapped pointer
    return (_Myptr);// 直接返回托管的指针
}

 

1.2 release方法

取消智能指针对动态内存的托管。

// 定义智能指针
shared_ptr<Test> test(new Test);

Test *tmp2 = test.release();    // 取消智能指针对动态内存的托管
delete tmp2;    // 之前分配的内存需要自己手动释放

// 函数原型
_Ty * release() noexcept
{    // return wrapped pointer and give up ownership
    _Ty * _Tmp = _Myptr; 
    _Myptr = nullptr;// 将成员指针设置为空
    return (_Tmp);// 将指针返回外界,由外界来控制是否delete
}

 

1.3 reset方法

重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉。

// 重置
p.reset() ; 将p重置为空指针,所管理对象引用计数 减1,若引用计数减少到0,空间会被销毁,会调用类的析构函数
p.reset(p1); 将p重置为p1(的值),p 管控的对象计数减1,p接管对p1指针的管控,若引用计数减少到0,空间会被销毁,会调用类的析构函数
p.reset(p1,d); 将p重置为p1(的值),p 管控的对象计数减1并使用d作为删除器,若引用计数减少到0,空间会被销毁,会调用类的析构函数
p1是一个指针!

// 交换
std::swap(p1,p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变
p1.swap(p2);    // 交换p1 和p2 管理的对象,原对象的引用计数不变

 

2.shared_ptr 介绍

记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,那么我们就释放它。

class Person {
public:
    Person(int v) {
        this->no = v;
        cout << "构造函数 \t no = " << this->no << endl;
    }

    ~Person() {
        cout << "析构函数 \t no = " << this->no << endl;
    }

private:
    int no;
};
int main() { 
    shared_ptr<Person> sp1;
    shared_ptr<Person> sp2(new Person(2));

    // 获取智能指针管控的共享指针的数量    use_count():引用计数
    cout << "sp1    use_count() = " << sp1.use_count() << endl;
    cout << "sp2    use_count() = " << sp2.use_count() << endl << endl;

    // 共享
    sp1 = sp2;

    cout << "sp1    use_count() = " << sp1.use_count() << endl;
    cout << "sp2    use_count() = " << sp2.use_count() << endl << endl;

    shared_ptr<Person> sp3(sp1); // 共同托管
    cout << "sp1    use_count() = " << sp1.use_count() << endl; // 可以打印引用计数值
    cout << "sp2    use_count() = " << sp2.use_count() << endl;
    cout << "sp2    use_count() = " << sp3.use_count() << endl << endl;

    return 0;
}

// 输出结果
构造函数         no = 2
sp1     use_count() = 0
sp2     use_count() = 1

sp1     use_count() = 2
sp2     use_count() = 2

sp1     use_count() = 3
sp2     use_count() = 3
sp2     use_count() = 3

析构函数         no = 2

 

2.1 使用make_shared构建对象

make_shared<类型>(构造函数参数列表);
shared_ptr<int> up3 = make_shared<int>(2); // 多个参数以逗号','隔开,最多接受十个
shared_ptr<string> up4 = make_shared<string>("字符串");
shared_ptr<Person> up5 = make_shared<Person>(9); //()内是构造函数的参数

会将对象指针和引用计数指针分配在一起,更高效。

 ref_count也需要一块堆空间,(这里vptr是什么?虚拟函数表的指针?为什么会放到这里?)

shared_ptr<Foo> x(new Foo);

这种方式需要为 Foo 和 ref_count 各分配一次内存,而使用

shared_ptr<Foo> x=make_shared<Foo>();

会一次分配较大的内存供 Foo 和 ref_count 对象容身,(存放到一块地址空间)。

3.注意事项

  • 在调用p.release()时会返回该指针,但不会释放p所指的内存,这时返回值就是对这块内存的唯一索引,如果没有使用这个返回值释放内存或是保存起来,这块内存就泄漏了。
  • 禁止delete 智能指针get 函数返回的指针。析构时造成重复释放
  • 禁止用任何类型智能指针get 函数返回的指针去初始化另外一个智能指针。具体解释:
//有问题的代码
std::shared_ptr<int> sp1(new int(10)); // 管理一个动态分配的整数
std::shared_ptr<int> sp4(sp1.get()); // 直接使用裸指针构造

sp1.get() 返回一个指向动态分配的整数的裸指针,但 shared_ptr 并不共享控制块和引用计数信息。如果 shared_ptr 通过裸指针构造,两个 shared_ptr 将独立管理同一个指针对象。这会导致两个 shared_ptr 在各自的生命周期结束时尝试释放相同的动态分配对象,从而造成双重释放问题,导致未定义行为(通常程序崩溃)。正确用法:

auto sp1 = std::make_shared<int>(10);
auto sp4 = sp1; // 正确: sp4 现在共享 sp1 的控制块和引用计数

通过拷贝构造函数来共享对象的所有权,确保引用计数正确管理对象的生命周期。

  • 不要定义指向智能指针的指针,这违背了智能指针的初衷。

 4.和const

4.1 const std::shared_ptr<T>

指针本身是常量,即你不能修改 shared_ptr 对象(例如,不能让它指向另一个对象),但通过这个指针可以修改它指向的对象。

    const shared_ptr<string> up4 = make_shared<string>("mystr");
    // 报错: error: no match for ‘operator=’ (operand types are ‘const std::shared_ptr<std::__cxx11::basic_string<char> >’ and ‘std::shared_ptr<std::__cxx11::basic_string<char> >’)
    up4 = make_shared<string>("mystr2");// 该行代码会报错,不能修改 `up4` 本身
    *up4 = "mmmm";
    cout<<*up4;// 输出mmmm,可以修改指向的内容

 

4.2 std::shared_ptr<const T>

指指针本身是可变的(即可以修改指针使其指向另一个对象),但不能修改它指向的对象,即所指对象是常量。当你希望通过 shared_ptr 提供只读访问权限时,可以使用 std::shared_ptr<const T>。

    shared_ptr<const string> up4 = make_shared<string>("mystr");
    up4 = make_shared<string>("mystr2");// 可以更改指向新的对象
    //error: assing ‘const std::__cxx11::basic_string<char>’ as ‘this’ argument discards qualifiers
    *up4 = "mmmm";//报错,不能修改
    cout<<*up4;

 

posted @ 2024-05-21 00:03  lypbendlf  阅读(8)  评论(0编辑  收藏  举报