2010-03-15 3 views
0

나는 op = 및 copy 생성자를 올바르게 수행하는 방법을 알고 있으므로, 적절하게 sort()으로 테스트 케이스를 작성했습니다. 작동 시키면, op =가 모든 data_을 하드 카피하고 있다는 것을 깨달았습니다.힙 할당 버퍼가있는 객체의 정렬 컨테이너 최적화 - 버퍼를 하드 복사하지 않으려면 어떻게해야합니까?

이 구조로 컨테이너를 정렬하려면 (힙 버퍼에 char 버퍼 배열이 할당되어 있음) 포인터를 교체하는 것이 더 빠를 것입니다. 그렇게 할 수있는 방법이 있습니까? 내 자신의 정렬/스왑 함수를 작성해야합니까?

#include <deque> 
//#include <string> 
//#include <utility> 
//#include <cstdlib> 
#include <cstring> 
#include <iostream> 
#include <fstream> 
#include <algorithm> // I use sort(), so why does this still compile when commented out? 

#include <boost/filesystem.hpp> 
#include <boost/foreach.hpp> 

using namespace std; 

namespace fs = boost::filesystem; 

class Page 
{ 
    public: 
     // constructor 
     Page(const char* path, const char* data, int size) : 
      path_(fs::path(path)), 
      size_(size), 
      rawdata_(new char[size]) 
     { 
//   cout << "Creating Page..." << endl; 
      strncpy(rawdata_, data, size); 
//   cout << "done creating Page..." << endl; 
     } 
     // copy constructor 
     Page(const Page& other) : 
      path_(fs::path(other.path())), 
      size_(other.size()), 
      rawdata_(new char[other.size()]) 
     { 
//   cout << "Copying Page..." << endl; 
      strncpy(data_, other.data(), size_); 
//   cout << "done copying Page..." << endl; 
     } 
     // destructor 
     ~Page() { delete[] data_; } 
     // accessors 
     const fs::path& path() const { return path_; } 
     const char* data() const { return rawdata_; } 
     int size() const { return size_; } 
     // operators 
     Page& operator = (const Page& other) { 
      if (this == &other) 
       return *this; 
      char* newImage = new char[other.size()]; 
      strncpy(newImage, other.data(), other.size()); 
      delete[] data_; 
      rawdata_ = newImage; 
      path_ = fs::path(other.path()); 
      size_ = other.size(); 
      return *this; 
     } 
     bool operator < (const Page& other) const { return path_ < other.path(); } 
    private: 
     fs::path path_; 
     int size_; 
     char* rawdata_; 
}; 

class Book 
{ 
    public: 
     Book(const char* path) : 
      path_(fs::path(path)) 
     { 
      cout << "Creating Book..." << endl; 
      cout << "pushing back #1" << endl; 
      // below, the RawData will be coming from methods like 
      // fstream.read(char* buffer, int filesize); or 
      // unzReadCurrentFile(unzFile zipFile, char* buffer, int size); 
      pages_.push_back(Page("image1.jpg", "firstImageRawData", 17)); 
      cout << "pushing back #3" << endl; 
      pages_.push_back(Page("image3.jpg", "thirdImageRawData", 17)); 
      cout << "pushing back #2" << endl; 
      pages_.push_back(Page("image2.jpg", "secondImageRawData", 18)); 

      cout << "testing operator <" << endl; 
      cout << pages_[0].path().string() << (pages_[0] < pages_[1]? " < " : " > ") << pages_[1].path().string() << endl; 
      cout << pages_[1].path().string() << (pages_[1] < pages_[2]? " < " : " > ") << pages_[2].path().string() << endl; 
      cout << pages_[0].path().string() << (pages_[0] < pages_[2]? " < " : " > ") << pages_[2].path().string() << endl; 

      cout << "sorting" << endl; 
      BOOST_FOREACH (Page p, pages_) 
       cout << p.path().string() << endl; 
      sort(pages_.begin(), pages_.end()); 
      cout << "done sorting\n"; 
      BOOST_FOREACH (Page p, pages_) 
       cout << p.path().string() << endl; 

      cout << "checking datas" << endl; 
      BOOST_FOREACH (Page p, pages_) { 
       char data[p.size() + 1]; 
       strncpy((char*)&data, p.data(), p.size()); 
       data[p.size()] = '\0'; 
       cout << p.path().string() << " " << data << endl; 
      } 
      cout << "done Creating Book" << endl; 
     } 

     const Page& getFirstPage() { return pages_[0]; } 
    private: 
     deque<Page> pages_; 
     fs::path path_; 
}; 

int main() { 
    Book* book = new Book("/some/path/"); 
    // below is an example of where the rawdata is used 
    // by a method that has a char* parameter 
    ofstream outFile("outimage.jpg"); 
    outFile.write(book->getFirstPage().data(), book->getFirstPage().size()); 
} 
+0

'std :: string'을 사용하지 않는 이유가 있습니까? – GManNickG

+0

또한, main에서'book'을 절대 삭제하지 않으므로 모든 작업이 훼손됩니다. :) – GManNickG

+0

메인의 책은 실제로 초점이 아닙니다. 파일 스트림에 std :: string을 사용해야합니까? 읽을 수있는 문자 스트림이 아닙니다. 파일 I/O를 배웠을 때 항상 fstream을 사용하고 char 버퍼를 만들었습니다. 게다가, 나는 압축되지 않은 unrar 라이브러리에서 스트림을 얻고 있는데, 원시 char *를 요청한다. – Kache

답변

0

불필요한 두통이 될 수 있으므로이 시나리오에서는 원시 char *를 사용하지 않습니다. std::string을 대신 사용하십시오. 그러면 컴파일러에서 생성 한 컴파일러, 할당 연산자 및 소멸자가 필요하지 않으므로 복사 생성자, 할당 연산자 및 소멸자가 필요하지 않습니다.

데이터를 복사하는 것이 여전히 큰 병목 현상 인 경우 일반적인 사용에서 추가 수준의 간접 참조로 살 수있는 경우 boost::shared_ptr을 사용하여 문자열을 보유 할 수 있습니다. 이렇게하면 포함 된 객체가 복사되고 RAII의 안전성을 여전히 얻으면 문자열이 복사되지 않습니다.

+0

사실, 트리오를 완전히 제거 할 것입니다. – GManNickG

+0

예, 수정 사항이 반영된 게시물입니다. –

+0

(위에서 언급 한 내용에서 반복 되겠지요.) 파일 스트림에 std :: string을 사용해야합니까? 어떻게하면 원시 char 포인터를 요구하는 unzip 및 unrar 라이브러리를 처리해야합니까? 또한 sort()에서 복사를 피하려면 boost :: shared_ptr가 할 수 있습니까? (나는 전에 그것을 사용한 적이 없다.) 나는 그것에 대해 조금 읽었다. 그것은'Page'가 이동/정렬 된 후에도 올바른 힙을 가리키는 내'Page's 안에 넣을 수있는 작은 포인터와 같은 객체를 만듭니다. – Kache

0

수동 char * 조작을 사용하는 것이 연습을위한 기준의 일부가 아닌 경우 std :: string을 사용하여 모든 할당 문제를 처리하도록 할 수 있습니다. std :: sort가 사용하는 std :: swap 함수는 std :: string :: swap을 호출하기 위해 특수화 된 것입니다. 즉, 자동으로 딥 복사가 아닌 문자열 데이터에 대한 포인터 만 바꿉니다.

운동을 목적으로 char *를 사용하려는 경우 아마도 두 개의 페이지 참조를 취하고 내부 데이터 포인터를 바꿔주는 독립 실행 형 스왑 함수를 만드는 가장 쉬운 방법 일 수 있습니다. 필자는 정렬이 표준 템플릿보다 더 나은 일치를 볼 수있는 한 증가 된 성능을 얻는 대신 함수를 호출 할 것이라고 생각합니다.

마지막으로, 헤더에 대한 질문에 대답하십시오. 컴파일러는 다른 헤더 (일반적으로 예상되지 않을 수도 있음)를 포함하는 실제 파일로 헤더를 자유롭게 구현할 수 있습니다. 거의 확실하게 iostream 헤더에는 알고리즘이 직접 또는 간접적으로 포함됩니다. 다른 컴파일러에서는 코드를 컴파일하지 못할 수도 있습니다.

+0

재미있는 점은 내가 이런 종류의 일을하는 것을 배웠을 때, 나는 이런 식으로 배웠다. 왜냐하면 그것들은 내가해야만하는 연습 이었기 때문이다. 그래서'myFstream.read (buffer, filesize) '와 같은 원하는 스트림을주는 함수는 첫 번째 매개 변수에'char *'를 원합니다. 대신 중간'char *'배열을 사용하지 않고 어떻게'std :: string'을 대신 넣을까요? – Kache

관련 문제