C++高级编程----对象池实例

本文探讨了C++代码优化的两个层面:语言层次和设计层次。在语言层次上,强调了按引用传递、返回和捕获异常的效率优势,以及适当使用内联方法。在设计层次,提到了利用STL,缓存策略和对象池来提高性能。通过一个对象池的实例,展示了如何减少构造和析构函数的调用次数,实现了资源的有效复用。
摘要由CSDN通过智能技术生成

背景:当尝试使用C++进行代码优化时,可以考虑从语言层次和设计层次进行性能优化。
1)语言层次:语言层次可以考虑从形参传递方式和返回值类型以及异常类型进行控制。

  1. 按引用传递:由于C++按值传递会有一定的复制开销,而按引用可以避免复制开销,如果不希望改变引用值,建议进行const修饰;对于复杂对象的创建(销毁),按值引用可能带来不必要地调用其他成员变量的构造函数(析构函数)
  2. 按引用返回:同样从函数的返回的角度考虑,按引用传递能避免不必要的复制开销;自C++11之后支持移动语义,允许高效地使用按值返回对象;
  3. 按引用捕捉异常:通过引用捕捉异常,以避免不必要的开销;通常抛出异常的开销很大,考虑使用引用,提升可能出现异常的情况;
  4. 恰当使用内联方法和函数:内联方法和函数的代码直接插入到被调用的位置,避免了函数调用的开销;由于内联函数的请求由编译器确定是否执行,只有熟悉一定的编译器文档才能恰当使用inline关键词,提升代码性能;

2)设计层次:

  1. 尽可能使用已有的数据结构和算法,尤其是STL标准库;
  2. 尽可能多的缓存:如果任务或计算执行特别慢,应该确保不会执行不必要的重复计算;将将来可能使用到的数据缓存下来,为下一次提供支持;可能存在缓存不同步问题(缓存失效),需要有一定的检测规则,确保缓存更新;
  3. 使用对象池:类似缓存原理,对需要经常和大量访问的对象设计对象池,实现一次创建,多次重复调用,从而避免不必要的构造函数和析构函数调用;

设计一个泛型对象池,创建一个大小为10的对象池,重复调用对象100次,记录观察构造函数和析构函数的调用次数;

// ObjectPool.h
#pragma once

#include<queue>
#include<memory>

template<typename T>
class ObjectPool{
   public:
       // constructor for default chunszie;
       ObjectPool(size_t chunkSize = kDefaultChunkSize);
       
       // prevent assignment and pass-by-value
       ObjectPool(const ObjectPool<T>& src) = delete;
       ObjectPool<T>& operator=(const ObjectPool<T>& rhs) = delete;
       using Object = std::shared_ptr<T>;
       // acuquire the pointer of queue.front()
       Object acquireObject();
   private:
       std::queue<std::unique_ptr<T>> mFreeList;
       size_t mChunkSize;

       static const size_t kDefaultChunkSize = 10;
       // fill the queue with mChunksize default value
       void allocateChunk();
};

template<typename T>
const size_t ObjectPool<T>::kDefaultChunkSize;

template<typename T>
ObjectPool<T>::ObjectPool(size_t chunkSize){
   if(chunkSize == 0){
       throw std::invalid_argument("chunk size must be positive. ");
   }
   mChunkSize = chunkSize;
   allocateChunk();
}

template<typename T>
void ObjectPool<T>::allocateChunk(){
   for(size_t i = 0; i < mChunkSize; ++i){
       mFreeList.emplace(std::make_unique<T>());
   }
}

template<typename T>
typename ObjectPool<T>::Object ObjectPool<T>::acquireObject(){
   if(mFreeList.empty()){
       allocateChunk();
   }
   std::unique_ptr<T> obj(std::move(mFreeList.front()));
   mFreeList.pop();
   Object smartObject(obj.release(), [this](T* t){mFreeList.push(std::unique_ptr<T>(t));});
   return smartObject;
}

测试用例

// main.cpp
#include<iostream>
#include"objectPool.h"

using namespace std;

class UserRequest{
    public:
    UserRequest(){mID = ++msCount; cout << "constructor : " << mID << endl;}
    virtual ~UserRequest(){cout << "destructor : " << mID << endl;}
    private:
        int mID;
        static int msCount;
};

int UserRequest::msCount = 0;

ObjectPool<UserRequest>::Object obtainUserRequest(ObjectPool<UserRequest>& pool){
    auto request = pool.acquireObject();
    return request;
}

void processUserRequest(ObjectPool<UserRequest>::Object& req){
    return req.reset();
}

int main(){
    ObjectPool<UserRequest> requestPool(10);
    for(size_t i = 0; i < 100; ++i){
        auto req = obtainUserRequest(requestPool);
        processUserRequest(req);
    }
    cout << "done. " << endl;
    return 0;
}

结果:
在这里插入图片描述
测试结果显示,尽管对象被调用了100,而对象池只是重复创建和销毁对象,只调用了10次构造函数和析构函数。注意设计中share_ptr和unique_ptr的使用情况,在队里中使用共享指针,每次请求都是从队列前端取对象发送给请求方。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值