欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > C++11QT复习 (十七)

C++11QT复习 (十七)

2025/11/15 2:35:15 来源:https://blog.csdn.net/m0_49013185/article/details/147080926  浏览:    关键词:C++11QT复习 (十七)

文章目录

    • Day12-1 智能指针回顾
      • 一、问题回顾
      • 二、智能指针
        • 1. `unique_ptr`
          • 构建右值的两种方式:
          • 构建左值的两种方式:
        • 2. `shared_ptr`
        • 3. 循环引用与 `weak_ptr`
        • 4. `weak_ptr`
        • 5.检查 `weak_ptr` 管理的对象是否还存在的方法
      • 三、删除器(Deleter)
        • 🌟 `std::default_delete`
          • 函数形式:
        • ✨ 自定义删除器示例(以 `FILE*` 为例)
          • ✅ 推荐用法(自动释放):
      • 四、智能指针的误用(⚠️易错点)
        • 🔥 示例类 Point
        • ❌ `unique_ptr` 的误用
        • ❌ `shared_ptr` 的误用
        • ✅ 正确使用 `shared_from_this`
          • ✅ 正确的 `addPoint` 函数:

Day12-1 智能指针回顾

一、问题回顾

  1. 什么是右值引用?有什么特点?右值引用本身是左值还是右值?
  2. 左值是什么?右值是什么?
  3. 移动构造函数与移动赋值函数的形态是什么样的?
  4. std::move函数的实质是什么?是否具备移动的含义?
  5. RAII是什么?具有什么特征?
  6. 对象语义与值语义的区别是什么?
  7. auto_ptr 有什么缺陷?

二、智能指针

1. unique_ptr
  • 独占所有权,不支持拷贝,只支持移动。
  • 可以作为容器元素,因为具备移动语义。
#include <iostream>
#include <memory>
#include <vector>
using namespace std;void test()
{unique_ptr<int> up(new int(10));cout << "*up = " << *up << endl;// unique_ptr<int> up2 = up; // 错误:拷贝构造被禁用// up3 = up;                 // 错误:拷贝赋值被禁用unique_ptr<int> up3(new int(34));vector<unique_ptr<int>> vec;// vec.push_back(up); // 错误:不能拷贝vec.push_back(std::move(up)); // OK:使用移动语义vec.push_back(std::move(unique_ptr<int>(new int(20)))); // OKfor (const auto& value : vec) {cout << *value << endl;}
}
构建右值的两种方式:
  1. 显式调用构造函数创建临时对象
  2. 使用 std::move 将左值转为右值
构建左值的两种方式:
  1. 构造函数创建有名对象:Point pt(1, 2);
  2. 用右值引用绑定右值:Point&& rref = Point(1, 2);
//延长number的生命周期static int number = 10;
void func()
{//加大括号,缩短number的生命周期{int number = 10;//作用域cout << "number = " << number << endl;}int a = 20;cout << "a = " << a << endl;//业务逻辑
}

2. shared_ptr
  • 支持共享所有权,通过引用计数实现。
  • 支持拷贝和移动,可以安全地放入容器中。
#include <iostream>
#include <memory>
#include <vector>
using namespace std;class Point
{
public:Point(int ix = 0, int iy = 0) : _ix(ix), _iy(iy) {cout << "Point(int, int)" << endl;}~Point() {cout << "~Point()" << endl;}Point(const Point& rhs) : _ix(rhs._ix), _iy(rhs._iy) {cout << "Point(const Point&)" << endl;}Point& operator=(const Point& rhs) {cout << "Point& operator=(const Point&)" << endl;if (this != &rhs) {_ix = rhs._ix;_iy = rhs._iy;}return *this;}private:int _ix, _iy;
};void test()
{shared_ptr<int> sp(new int(10));cout << "*sp = " << *sp << ", use_count = " << sp.use_count() << endl;shared_ptr<int> sp2 = sp; // 拷贝共享所有权cout << "*sp2 = " << *sp2 << ", use_count = " << sp2.use_count() << endl;shared_ptr<int> sp3(new int(34));sp3 = sp; // 也可以赋值共享cout << "*sp3 = " << *sp3 << ", use_count = " << sp3.use_count() << endl;// 放入容器shared_ptr<Point> sp4(new Point(1, 2));vector<shared_ptr<Point>> vec;vec.push_back(std::move(sp4));vec.push_back(shared_ptr<Point>(new Point(3, 4)));shared_ptr<Point> sp5(new Point(7, 8));//实际上在push_back中传左值或者传右值都ok!vec.push_back(sp5);// 总结:shared_ptr 拥有拷贝和移动语义
}

3. 循环引用与 weak_ptr
  • 问题:循环引用会造成内存泄漏。
  • 解决方案:用 shared_ptr 搭配 weak_ptr,避免引用计数无法归零。
#include <iostream>
#include <memory>
using namespace std;class Child; // 前向声明class Parent
{
public:Parent() { cout << "Parent()" << endl; }~Parent() { cout << "~Parent()" << endl; }shared_ptr<Child> pChild;
};class Child
{
public:Child() { cout << "Child()" << endl; }~Child() { cout << "~Child()" << endl; }weak_ptr<Parent> pParent; // 使用 weak_ptr 避免引用计数递增
};void test()
{shared_ptr<Parent> parentPtr(new Parent());shared_ptr<Child> childPtr(new Child());//解决内存泄漏的问题parentPtr->pChild = childPtr;childPtr->pParent = parentPtr;cout << "parentPtr.use_count() = " << parentPtr.use_count() << endl;cout << "childPtr.use_count() = " << childPtr.use_count() << endl;
}

在这里插入图片描述


4. weak_ptr
  • 不能直接拥有资源,不增加引用计数。
  • 常用于观察对象生命周期或解决循环引用问题。
#include <iostream>
#include <memory>
using namespace std;class Point
{
public:Point(int ix = 0, int iy = 0) : _ix(ix), _iy(iy) {cout << "Point(int,int)" << endl;}~Point() { cout << "~Point()" << endl; }Point(const Point& rhs) : _ix(rhs._ix), _iy(rhs._iy) {cout << "Point(const Point&)" << endl;}Point& operator=(const Point& rhs) {cout << "Point& operator=(const Point&)" << endl;if (this != &rhs) {_ix = rhs._ix;_iy = rhs._iy;}return *this;}private:int _ix, _iy;
};void test()
{/*weak_ptr必须从一个shared_ptr或者另一个weak_ptr来构造。weak_ptr本身并没有获得资源的所有权,所以它不能直接持有通过new创建的对象的所有权,因为它不会增加引用计数,也无法管理对象的释放。*///std::weak_ptr<Point> wp2(new Point(3, 4));//weak_ptr创建的时候,不能直接与托管资源产生联系std::weak_ptr<Point> wp;//空的 weak_ptr;可以创建空对象std::shared_ptr<Point> sp(new Point(1, 2));wp = sp;// weak_ptr 可以通过 shared_ptr 构造std::unique_ptr<Point> pt;// 不允许用 unique_ptr 构造 weak_ptr
}
int main()
{test();return 0;
}

5.检查 weak_ptr 管理的对象是否还存在的方法
方法名功能简介
use_count()返回共享对象的引用计数(观察 shared_ptr 是否还存在)
expired()判断对象是否已销毁,相当于 use_count() == 0
lock()尝试将 weak_ptr 升级为 shared_ptr,若对象已销毁则返回空指针

示例代码:

#include <iostream>
#include <memory>
using namespace std;int main() {shared_ptr<int> sp = make_shared<int>(42);weak_ptr<int> wp = sp;cout << "use_count = " << wp.use_count() << endl;  // 输出 1cout << "expired = " << boolalpha << wp.expired() << endl;  // 输出 falseif (shared_ptr<int> temp = wp.lock()) {cout << "对象存在,值为:" << *temp << endl;} else {cout << "对象已销毁" << endl;}sp.reset();  // 手动释放 shared_ptrcout << "\n释放后:\n";cout << "use_count = " << wp.use_count() << endl;  // 输出 0cout << "expired = " << wp.expired() << endl;      // 输出 trueif (shared_ptr<int> temp = wp.lock()) {cout << "对象存在,值为:" << *temp << endl;} else {cout << "对象已销毁" << endl;}return 0;
}

三、删除器(Deleter)

🌟 std::default_delete

std::default_deleteunique_ptrshared_ptr 默认使用的删除器。其重载了 operator(),分别适用于普通指针和数组指针:
在这里插入图片描述

函数形式:
// 1. 删除普通对象
void operator()(T* ptr) const;// 2. 删除数组对象(只有当 T 是数组类型时)
template<class U>
void operator()(U* ptr) const;
  • (1) 调用 delete ptr;
  • (2) 调用 delete[] ptr;,仅当 U()[] 可以隐式转换为 T()[] 时生效。
  • 如果 U 是不完整类型(incomplete type),程序会报错。
  • 无异常抛出保证(noexcept)

✨ 自定义删除器示例(以 FILE* 为例)

说明:
FILE* 不能使用默认的 delete 释放,必须使用 fclose。所以我们定义一个自定义删除器 FileCloser

✅ 推荐用法(自动释放):
#include <memory>
#include <cstdio>
#include <iostream>
#include <string>struct FileCloser {void operator()(FILE* fp) const {if (fp) {fclose(fp);std::cout << "fclose(fp)" << std::endl;}}
};void test() {std::string msg = "hello, world\n";std::unique_ptr<FILE, FileCloser> uptr(fopen("wd.txt", "a+"));if (uptr) {fwrite(msg.c_str(), 1, msg.size(), uptr.get());}// 自动调用 FileCloser 析构,自动 fclose
}void test2() {std::string msg = "hello, world\n";std::shared_ptr<FILE> sptr(fopen("wd.txt", "a+"), FileCloser());//先创建对象再传递参数的方法也是ok的,区别在于一个是传右值,另一个是传右值/*FileCloser fc;std::shared_ptr<FILE> sptr(fopen("wd.txt", "a+"), fc);*/if (sptr) {fwrite(msg.c_str(), 1, msg.size(), sptr.get());}// 自动调用 FileCloser 析构,自动 fclose
}

四、智能指针的误用(⚠️易错点)

🔥 示例类 Point
class Point : public std::enable_shared_from_this<Point> {
public:Point(int ix = 0, int iy = 0) : _ix(ix), _iy(iy) {std::cout << "Point(int,int)" << std::endl;}~Point() { std::cout << "~Point()" << std::endl; }Point(const Point& rhs) : _ix(rhs._ix), _iy(rhs._iy) {std::cout << "Point(const Point&)" << std::endl;}Point& operator=(const Point& rhs) {std::cout << "Point& operator=(const Point&)" << std::endl;if (this != &rhs) {_ix = rhs._ix;_iy = rhs._iy;}return *this;}// ⚠️ 原始写法(误用):返回 this 的裸指针Point* addPoint(Point* pt) {_ix += pt->_ix;_iy += pt->_iy;return this;}void print() const {std::cout << "(" << _ix << ", " << _iy << ")" << std::endl;}private:int _ix, _iy;
};

unique_ptr 的误用
void test1() {Point* pt = new Point(1, 2);std::unique_ptr<Point> up(pt);std::unique_ptr<Point> up2(pt);  // ❌ 错误!双重释放
}void test2() {std::unique_ptr<Point> up(new Point(1, 2));std::unique_ptr<Point> up2(new Point(3, 4));up.reset(up2.get());  // ❌ 错误!up 和 up2 同时管理同一指针
}

shared_ptr 的误用
void test3() {Point* pt = new Point(1, 2);std::shared_ptr<Point> sp(pt);std::shared_ptr<Point> sp2(pt);  // ❌ 错误!会导致两次 delete
}void test4() {std::shared_ptr<Point> sp(new Point(1, 2));std::shared_ptr<Point> sp2(new Point(3, 4));sp.reset(sp2.get());  // ❌ 错误!sp 和 sp2 同时管理同一资源
}

✅ 正确使用 shared_from_this
void testAdd() {std::shared_ptr<Point> sp(new Point(1, 2));std::shared_ptr<Point> sp2(new Point(3, 4));std::shared_ptr<Point> sp3(sp->addPoint(sp2.get()));sp3->print();
}

⚠️ 这需要 addPoint() 返回一个 shared_ptr<Point>,否则 sp3 会重复管理裸指针(this)。

✅ 正确的 addPoint 函数:
// 改为返回 shared_ptr 并使用 shared_from_this()
std::shared_ptr<Point> addPoint(Point* pt) {_ix += pt->_ix;_iy += pt->_iy;return shared_from_this();  // 转换为 shared_ptr,安全共享
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词