代码之家  ›  专栏  ›  技术社区  ›  Kache

提取原始数据,然后将其传递到另一个类中——如何在保持封装的同时避免复制两次?

  •  5
  • Kache  · 技术社区  · 14 年前

    考虑一下 class Book 用钢制容器 class Page . 每个 Page 截图,比如 page10.jpg 生料 vector<char> 形式。

    Book 以包含这些截图的zip、rar或目录的路径打开,并使用各自的方法提取原始数据,如 ifstream inFile.read(buffer, size); unzReadCurrentFile(zipFile, buffer, size) . 然后它调用 Page(const char* stream, int filesize) 建造师。

    现在,很明显原始数据被复制了两次。一次提取到本地 buffer 第二次在 Ctoto Page::vector<char> . 有没有一种方法可以在去掉中间人缓冲区的同时保持封装?

    5 回复  |  直到 14 年前
        1
  •  3
  •   Steve Jessop    14 年前

    对于基于您已经拥有的代码更改,最简单的方法可能是给page一个采用非常量向量引用或指针的setter,以及 swap 它包含在页面中的向量。调用者将保留一个空向量,但由于问题是过度复制,因此可能调用者没有 希望 要保留数据:

    void Book::addPage(ifstream file, streampos size) {
        std::vector<char> vec(size);
        file.read(&vec[0], size);
        pages.push_back(Page()); // pages is a data member
        pages.back().setContent(vec);
    }
    
    class Page {
        std::vector<char> content;
    public:
        Page() : content(0) {} // create an empty page
        void setContent(std::vector<char> &newcontent) {
            content.swap(newcontent);
        }
    };
    

    有些人(例如谷歌C++风格指南)希望引用参数是const,并希望您通过 newcontent 参数作为指针,以强调它是非常量:

    void setContent(std::vector<char> *newcontent) {
        content.swap(*newcontent);
    }
    

    掉期 速度很快——您会期望它只是交换两个向量对象的缓冲区指针和大小。

    或者,给page两个不同的构造函数:一个用于zip文件,另一个用于常规文件,并让它负责读取自己的数据。这可能是最干净的,它允许页面是不可变的,而不是在构造之后被修改。但实际上,您可能不希望这样做,因为正如您在注释中注意到的,将页面添加到容器中会复制页面。因此,修改页面以添加数据是有好处的 之后 它在容器中构造得很便宜:它避免了额外的拷贝,而不需要处理指针容器。尽管如此, setContent 函数可以很容易地获取文件流/zip文件信息,就像获取向量一样。

    您可以找到或编写一个从ZipFile读取的流类,这样页面就可以只使用一个构造函数读取流来负责读取数据。或者可能不是整个流类,也可能只是您设计的一个接口,它将数据从流/zip/rar读取到指定的缓冲区中,而page可以将其内部向量指定为缓冲区。

    最后,您可以“处理指针容器”。制作 pages std::vector<boost::shared_ptr<Page> > ,然后执行:

    void Book::addPage(ifstream file, streampos size) {
        boost::shared_ptr<Page> page(new Page(file, size));
        pages.push_back(page); // pages is a data member
    }
    

    A shared_ptr 相对于一个页面(它为包含指针和refcount的小节点进行额外的内存分配),开销适中,但是 许多的 复制成本更低。它也在TR1中,如果你有一些实现,而不是Boost。

        2
  •  2
  •   Brian R. Bondy    14 年前

    使用 std::vector resize 成员最初设置缓冲区大小,然后使用 front() 的地址。

    std::vector<char> v;
    v.resize(size);
    strcpy(&v.front(), "testing");
    

    直接缓冲访问 标准::矢量 由: &v.front()

        3
  •  2
  •   cuteCAT    14 年前

    使用std::vector保存图像数据是一个坏主意。为此,我将使用原始指针或共享指针。这样可以防止缓冲区被复制两次。

    自从你 关心记忆,把所有的图像数据保存在记忆中对我来说也是个坏主意。更好的情况是将它封装到一个单独的类中。例如,ImageData。此类包含图像数据的行指针。该类可以首先用文件路径初始化,并在需要时从磁盘加载图像数据。

        4
  •  1
  •   Josh Townzen    14 年前

    我要的是 Page 类直接从源读取自己的数据,并且 Book 只读取所需的源代码,以便定位每个单独的页(并读取属于 一般来说,如标题)。

    例如,如果数据存储在目录中,则 将检索目录中的文件列表。对于每个文件,它将把文件名传递给 将打开文件并加载其内容的构造函数。

    至于这本书存储在zip文件中的情况,我正在猜测您使用的库是如何工作的。我觉得你用的是MiniZip,我不熟悉,但乍一看,通过MiniZip打开一个文件会给你一个处理方法。你把把手递给 unzGoToFirstFile() unzGoToNextFile() 要在zip文件中设置活动子文件(在您的情况下是活动页),请使用 unzReadCurrentFile() 将活动子文件加载到缓冲区中。如果是这样,那么你的 类将使用MiniZip打开文件并将其设置为第一个子文件。然后它会将句柄传递给zip文件中的构造函数。 这将完成从zip文件读取子文件的工作。这个 然后打电话 unzgotonextfile()。 移动到下一个子文件,并通过再次将句柄传递给 . 它将继续这样做,直到没有剩余的子文件。它看起来像:

    Page::Page(zipFile file)
    {
        //  TODO:  Determine the required size of the buffer that will store the data
        unsigned buffer_size;
    
        data_.resize(buffer_size)
    
        unzReadCurrentFile(file, &data_[0], buffer_size);
    }
    
    void Book::open(const std::string &filename)
    {
        zipFile file = unzOpen(filename.c_str());
    
        int result = unzGoToFirstFile(file);
        while (result == UNZ_OK)
        {
            pages_.push_back(Page(file));
            unzGoToNextFile(file);
        }
    }
    

    这是非常简单的(我可能完全错误地使用了MiniZip,所以要小心),它还假定 存储矢量 对象命名 pages_ 命名缓冲区 data_ .

        5
  •  0
  •   wilhelmtell    14 年前

    您可以引入第三个组件来保存所有图像。这本书会把它填满,书页会从中读出来。如果你想限制访问,你可以关闭它,让书和网页的朋友。如果你有重复的图片(比如,每一页都有一个页脚和页眉,或者有些页面有一个徽标,或者其他什么),你可以把第三个组件变成一个飞锤,从而使它比你所追求的更有效率。

    打开书本时,一定不要打开所有的页面。那可能很贵。让每个页面保存其图像的标识符(可能是文件路径),并且仅当您真正想要查看页面时才加载图像。