实用指南:C++编程实践——标准库中容器存储目标分析

一、容器存储

在前面的文章中一个重要的推荐应用就是在可能的情况下尽量使用STL中的标准容器,这样除了安全以后,最重要的是通用性较高。但在实际的应用中,有很多场景下需要在容器中存储大量的目标对象,比如在高并发的网络下,服务端需要存储客户端的一些相关对象。而在这些应用中,这些目标对象有大有小,有平凡类型也有复杂的类型,甚至有可能会提出多态的需求。那么,问题就产生了,在向容器中存储目标时,是存储对象目标还是存储指针目标呢?
本文就针对这种实际工程中的应用开发进行分析和说明,从而能够给大家一个可以借鉴的途径和原则。

二、存储目标

刚刚提到,容器中既可以存储对象也可以存储指针,那么这两种情况下会有什么问题呢?

  1. 对象的存储
    直接存储对象轻松方便,但对于复杂类型,特别是存在内部的指针数据或者要求多态处理的情况下,就无法达到开发者的要求了。同样,要是对象的目标较大,在数据的数量达到一程度后,存储的开销也是一个问题。还有,对象生命周期也要限制与容器相匹配的值语义需求
  2. 指针的存储
    存储指针,容器中的存储大小是可控线性增长的(N*指针大小),一般上来讲,很难出现容器应付不足(因为对象可能先分配不出来了)。但使用指针最关键的是使得整体的操作变得复杂起来,开发者需要进行内存的主动回收。如果控制不好,就可能会出现多次释放内存或使用已经释放内存的情况。最后,启用指针则无法保证对象的生命周期与容器的生命周期相匹配(即双方生命周期互相独立)

在上面的两个问题基础上,又可以展开下面的问题:

  1. 内部指针的问题
    假如存储的对象内部存在指针类型素材怎么办?甚至更复杂的情况会有反复嵌套的情况。这就又引出一个经典的例子,深浅拷贝。如果此时存储对象,以vector为例,它会在push_back时,做拷贝的动作。如果类中未进行深拷贝的处理,很容易出现内部指针数据悬垂的现象,这是极其危险的。
  2. 多态处理
    而如果存储的类对象是存在多态行为的,而在实际的应用中,又希望能够借助容器的存储来动态的支持不同父子对象的行为处理。那么,存储指针是一种强选择了。
  3. 移动语义
    在C++11以后,提供了移动语义,那么在一些较大甚至大型的对象面前,是否行启用移动语义来实现对象的存储呢?目前看在有些场景下是可以满足需求的。

三、处理方法和原则

通过看到上面的分析,其实就能够通过下面的方式来处理存储的类型:

  1. 存储对象
    一般上来讲,如果是平凡类型或原来的POD类型,也就是说,这个对象只是基础类型的数据描述,而且对象的内存空间不是多大(到底什么是大和小,必须开发者根据实际场景来自己确定),容器存储尽量选择以对象的方式来存储。另外一些类对象可能相对简单,对象也不大,应用也没有多态等的要求,此时也可以考虑启用对象直接存储到容器中。
    这样运行起来直观、简单,也更容易维护。
  2. 存储指针(智能指针)
    假如容器存储的对象很大,此时一般是考虑存储指针。但存储指针就比有刚刚提到的内存操作风险问题,所以非特殊场景下(比如和C库的接口交互),不推荐利用普通指针,而是采用智能指针。若是一定要运用普通指针,请采用RAII等技术保证内存资源的安全
  3. 多态等需求
    在需要应用多态的情况下,原则上推荐利用智能指针,这样既能够满足多态的需要又许可满足内存安全处理的需求
  4. 通过支撑移动语义,能够考虑启用对象
    若是开发者已经在C++11以后开发,在自己希望应用大对象时,可考虑使用移动语义来满足在容器中存储对象的要求。但尽量要满足其它的限制条件,不能因为可以而认为一定行
  5. 内部有指针资料
    当类中存在指针数据,即类中嵌套有对象指针。此时有两种情况,如果利用的是智能指针,则行等同于对“存储指针”的处理。但如果存储的是普通指针,则需进行深拷贝的处理(编写拷贝函数)
  6. 存储对象需要共享
    推荐启用智能指针。这样可以极大的减少拷贝或内存安全挑战就是如果容器中存储的目标需要为多个场景共享,此时一般

否对缓存友好。就是通过上面的分析,整体的把控原则就是:考虑拷贝的开销控制并兼顾移动语义来满足性能需求;支持多态和共享来达到应用目的;保证内存资源的安全管理和

四、例程

下面看一个工程中实践的代码:

#ifndef DATAPARSE_MEMCACHEEX_H
#define DATAPARSE_MEMCACHEEX_H
#include <string>
  #include <unordered_map>
    #include <memory>
      #include <vector>
        #include <queue>
          #include <mutex>
            #include <assert.h>
              #include "MemCache.h"
              namespace dataparse
              {
              template<typename T,typename U,typename E>
                class MemCacheEx
                {
                public:
                MemCacheEx() {}
                ~MemCacheEx() {}
                public:
                int InitMemCache(int len = global::CACHE_NUM)
                {
                int num = this->CreateMemCache(len);
                return num;
                }
                std::shared_ptr<U> QueryAskMap(const T &id, const T &pbkey)
                  {
                  auto au = this->umemAsk_.find(id);
                  if (au != this->umemAsk_.end())
                  {
                  return au->second;
                  }
                  return nullptr;
                  }
                  std::shared_ptr<U> QueryAckMap(const T &id, const T &pbkey)
                    {
                    auto au = this->umemAck_.find(id);
                    if (au != this->umemAck_.end())
                    {
                    return au->second;
                    }
                    return nullptr;
                    }
                    //此处的ID为MD5值,不再是USERID,其它同样
                    int AddMsgToMemCache(const T &id, const T & userid, char  *buf, int len, CacheType type)
                    {
                    //缓存单元赋值
                    //增加对长度的判断,如果超过10万条就删除索引在前面的一条		
                    std::shared_ptr<U> pd = nullptr;
                      bool exist = true;
                      //已经存在
                      if (CacheType::ASK == type)
                      {
                      pd = this->QueryAskMap(id,"");
                      }
                      else
                      {
                      pd = this->QueryAckMap(id,"");
                      }
                      //全新存储
                      if (nullptr == pd)
                      {
                      exist = false;
                      pd = this->GetMsgCacheDataFromQueue();
                      pd->md_ = id;
                      pd->userid_ = userid;
                      pd->index_ = ++this->count_;
                      }
                      E cd;
                      cd.len = len;
                      memmove(cd.buf, buf, len);
                      pd->vec_.emplace_back(cd);
                      if (exist)
                      {
                      return 1;
                      }
                      //存储MAP
                      if (CacheType::ASK == type)
                      {
                      this->umemAsk_.emplace(std::pair<T, std::shared_ptr<U>>(id, pd));
                        }
                        else
                        {
                        this->umemAck_.emplace(std::pair<T, std::shared_ptr<U>>(id, pd));
                          }
                          return 0;
                          }
                          int DeleteMsgFromMemCache(const T & id, CacheType type)
                          {
                          //删除
                          std::unordered_map<T, std::shared_ptr<U>> * tmp = nullptr;
                            if (type == CacheType::ASK)
                            {
                            tmp = &this->umemAsk_;
                            }
                            else
                            {
                            tmp = &this->umemAck_;
                            }
                            auto search = tmp->find(id);
                            if (search != tmp->end())
                            {
                            this->SetMsgCacheDataToQueue(search->second);
                            //删除
                            tmp->erase(search);
                            return 1;
                            }
                            else
                            {
                            return 0;
                            }
                            }
                            int GetMemCacheSize(CacheType ct)
                            {
                            if (ct == CacheType::ACK)
                            {
                            return this->umemAck_.size();
                            }
                            else if(ct == CacheType::ASK)
                            {
                            return this->umemAsk_.size();
                            }
                            return 0;
                            }
                            //聊天转发缓存
                            std::shared_ptr<U> GetMemFromCache()
                              {
                              std::shared_ptr<U>  tmp = nullptr;
                                if(this->queue_.size() > 0)
                                {
                                std::lock_guard<std::mutex> guard(this->mutex_);
                                  tmp = this->queue_.front();
                                  this->queue_.pop();
                                  }
                                  else
                                  {
                                  std::lock_guard<std::mutex> guard(this->mutex_);
                                    tmp = std::make_shared<U>();
                                      }
                                      assert(tmp);
                                      return tmp;
                                      }
                                      void SetMemToCache(std::shared_ptr<U> p)
                                        {
                                        //初始化
                                        p->len = 0;
                                        p->type = 0;
                                        p->ptmp = nullptr;
                                        p->pfind = nullptr;
                                        this->queue_.emplace(p);
                                        }
                                        private:
                                        int CreateMemCache(int len)
                                        {
                                        for (int num = 0; num < len; num++)
                                        {
                                        std::shared_ptr<U> pCache = std::make_shared<U>();
                                          this->queue_.emplace(pCache);
                                          }
                                          return len;
                                          }
                                          std::shared_ptr<U> GetMsgCacheDataFromQueue()
                                            {
                                            std::lock_guard<std::mutex> lock(this->mutex_);
                                              if (this->queue_.size() < 5)
                                              {
                                              this->CreateMemCache(100);
                                              }
                                              std::shared_ptr<U> pd = this->queue_.front();
                                                this->queue_.pop();
                                                return pd;
                                                }
                                                int SetMsgCacheDataToQueue(std::shared_ptr<U> pd)
                                                  {
                                                  //初始化可以用相关数据对象的本身来处理
                                                  std::lock_guard<std::mutex> lock(this->mutex_);
                                                    pd->index_ = 0;
                                                    pd->md_ = "";
                                                    pd->userid_ = "";
                                                    pd->vec_.clear();
                                                    this->queue_.emplace(pd);
                                                    return 0;
                                                    }
                                                    private:
                                                    //请求消息队列和返回结果消息队列
                                                    std::unordered_map<T, std::shared_ptr<U>> umemAsk_;
                                                      std::unordered_map<T, std::shared_ptr<U>> umemAck_;
                                                        std::queue<std::shared_ptr<U>> queue_;
                                                          std::mutex mutex_;
                                                          int count_ = 0;  //index的计数
                                                          };
                                                          }
                                                          #endif // !DATAPARSE_MEMCACHEEX_H

代码非常简单,不再做分析说明。

五、总结

教材上的知识讲得很多,很细。但一个重要的问题在于,如何把理论的工艺知识和实践紧密的结合起来。如果一个一个的来试,肯定会遇到不少的坑。甚至有些技术点可能都无法方便的通过应用来验证。这就需要开发者能够处理如何把抽象的知识和实践应用借助某种巧妙的设计整合在一起,然后进行互相的反馈,从而能够迅速的将二者结合以达到真正理解并掌握的目的。
至于这个巧妙的设计从何而来?就需要不断的思考并学习别人的编写经验。特别是网上不错的开源代码。

posted @ 2026-01-23 18:08  gccbuaa  阅读(0)  评论(0)    收藏  举报