C++语法细节复习(2)

智能指针

其本质是利用栈上对象出作用域自动析构的特性自动释放堆上的内存.

weak_ptr / shared_ptr

  • weak_ptr : 弱智能指针, 观察者, 不影响引用计数, 作用在于观察一个指针, 并在关键时刻可以通过提升得到使用权.

  • shared_ptr : 强智能指针, 拥有者, 影响引用计数, 确实拥有并可以使用指针.

  • weak_ptr.lock() : 检测观察对象的引用计数, 不为0则将弱智能指针提升为强智能指针, 可以检测指针指向的资源是否还存在.

  • 可以很好地接近多线程环境下共享对象的线程安全问题, 主线程掌握shared_ptr, 调用其他线程时将shared_ptr强转成weak_ptr, 也就是说其他线程中都是观测者, 在想要使用时调用lock, 看是否还存在, 存在就提升为shared_ptr.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class A
    {
    public:
    A(int w) :x(w) {}
    void testA()
    {
    cout << "触发testA, x : " << x << endl;
    }
    private:
    int x;
    };

    void handler(weak_ptr<A> pw)
    {
    shared_ptr<A> sp = pw.lock();
    if(sp != nullptr) sp->testA();
    else cout << "A对象已析构, 不能再访问" << endl;
    }

    int main()
    {
    {
    shared_ptr<A> p(new A());
    thread t1(handler, weak_ptr(p));
    t1.detach();
    }
    return 0;
    }

unique_ptr

  • unique_ptr : 无引用计数, 拥有并独占一个指针.
  • 拷贝只支持移动拷贝构造, 会转移独占权.

make_shared

1
2
shared_ptr<T> sp1(new T(10));	// 1
auto sp2 = make_shared<T>(10); // 2

普通的shared_ptr构造存在缺陷 :

  • shared_ptr存在两部分资源, 一部分是所管理对象new出来的资源, 一部分是存放引用计数new出来的资源, 由于两部分都有可能出错, 如果出现部分成功部分失败就有内存泄漏的风险.

make_shared可以做到以下改进 :

  • 将管理对象和引用计数对象包装进一个对象, 只new这个对象, 可以保证全失败或全成功. 以此提高内存分配效率并且防止内存泄露风险.

make_shared缺点 :

  • 无法自定义删除器.
  • 托管的内存会延迟释放, 因为如果有弱智能指针会等到弱指针移除才全部析构.

deletor(自定义)

unique_ptr和shared_ptr等智能指针在要销毁内存时, 其析构函数其实是在调用默认的deletor, 类似如下 :

1
~unique_ptr() { deletor(ptr); }

而这个默认的deletor就重载了(), 其默认操作的就对ptr执行普通的delete.

自定义deletor的必要性在于, 有很多资源的释放方式并不是普通的delete, 那么这个这种默认方式就不可以用在这些资源上, 比如数组资源就要 delete [], 文件资源就要fclose, socket要close等.

因此我们可以通过写一个自定义deletor, 重载(), 就可以实现特殊资源的特殊释放.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template<typename T>
class MyDeletor
{
public:
void operator()(T* ptr) const
{
cout << "MyDeletor" << endl;
delete []ptr;
}
};

template<typename T>
class MyFileDeletor
{
public:
void operator()(T* ptr) const
{
cout << "MyFileDeletor" << endl;
fclose(ptr);
}
};

unique_ptr<int, MyDeletor<int>> ptr1(new int[100]);
unique_ptr<FILE, MyFileDeletor<FILE>> ptr2("data.txt", "w");

当然上面是旧做法, 现在我们可以通过function + lambda快捷实现 :

1
2
3
4
5
6
7
8
9
10
11
12
13
// 这里使用的是第二种构造函数 unique_ptr(T* ptr, Deleter d); 
// 这个Deleter要和前面第二个模板参数匹配, 这样就可以推导出lambda表达式的类型.
unique_ptr<int, function<void(int*)>> ptr1(new int[100],
[](int* ptr)->void{
cout << "lambda MyDeletor" << endl;
delete[] ptr;
});

unique_ptr<FILE*, function<void(FILE*)>> ptr2(fopen("data.txt", "w"),
[](FILE* ptr)->void{
cout << "lambda MyFileDeletor" << endl;
fclose(ptr);
});

还可以通过decltype + lambda实现 :

1
2
auto lambda = [](int* ptr){ delete[] ptr; };
std::unique_ptr<int, decltype(lambda)> ptr(new int[100], lambda);

函数绑定

function(绑定器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 模板 + 可变参数
template<typename R, typename... Args>
class myfunction<R(Args...)> // 函数类型实例化类模板
{
public:
using PFUNC = R(*)(Args...);
myfunction(PFUNC pfunc) :_pfunc(pfunc){}

R operator()(Args... arg)
{
return _pfunc(arg...);
}
private:
PFUNC _pfunc;
}
  • 绑定成员函数必须依赖于一个对象 :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    class Test
    {
    public:
    void hello(string str)
    {
    cout << str << endl;
    }
    };

    int main()
    {
    fuction<void(Test*, string)> func = &Test::hello;
    func(&Test(), "call Test::hello!");
    }

lambda

[捕获外部变量](形参列表)->返回值{代码}

  • [=] 以传值方式捕获外部所有变量.
  • [&] 以传引用方式捕获外部所有变量.
  • [=, &a] 除a传引用, 其他变量都传值.
  • [a, &b] a传值, b传引用.
  • [this] 捕获this指针
1
2
3
4
5
6
7
8
9
10
// 找第一个小于65的值
auto it = find_if(v.begin(), v.end(), [](int a){
return a < 65;
});
if(it != v.end()) cout << "find less than 65" << endl;

// 输出所有奇数
for_each(v.begin(), vec.end(), [](int a){
if(a & 1) cout << a << " ";
});
  • 可以用fuction类型表示lambda表达式类型.

    1
    2
    3
    map<int, function<int(int, int)>> cal;
    cal[1] = [](int a, int b){ return a + b; };
    cal[2] = [](int a, int b){ return a - b; };
  • 优先级队列自定义比较

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Data{
    public:
    Data(int v1, int v2) :a(v1) ,b(v2) {}
    private:
    int a, b;
    };

    using FUNC = function<bool(Data&, Data&)>;
    priority_queue<Data, vector<data>, FUNC> Heap([](Data& d1, Data&d2){
    return d1.ma > d2.ma;
    });

C++语法细节复习(2)
http://example.com/2024/05/06/C++语法细节复习(2)/
作者
天目中云
发布于
2024年5月6日
许可协议