2010-11-27 31 views
3

如何使用一個文件初始化一個全局常量對象,該對象太大而無法在堆棧中創建?這是迄今爲止我嘗試:初始化一個大的全局常量對象

// test.h 
#pragma once 
#include <boost/array.hpp> 

typedef boost::array<int,100000> bigLut_t; 
extern const bigLut_t constLut; 

// test.cpp 
#include <fstream> 
#include <boost/filesystem.hpp> 
#include "test.h" 

bigLut_t& initializeConstLut() 
{ 
    if(boost::filesystem::exists("my_binary_file") == false) { 
     std::ofstream outStream("my_binary_file", ios::out | ios::binary); 
     bigLut_t* tempLut = new bigLut_t; 
     for(int i = 0; i < 100000; ++i) { 
      // Imagine this taking a long time, 
      // which is why we're using a file in the first place 
      tempLut->at(i) = i; 
     } 
     outStream.write(reinterpret_cast<char*>(tempLut), sizeof(bigLut_t)); 
     outStream.close(); 
     delete tempLut; 
    } 
    // We can't write "bigLut_t lut;" because that would cause a stack overflow 
    bigLut_t* lut = new bigLut_t; // lut gets never deallocated 
    std::ifstream inStream("my_binary_file", ios::in | ios::binary); 
    inStream.read(reinterpret_cast<char*>(lut), sizeof(bigLut_t)); 
    inStream.close(); 
    return *lut; 
} 

const bigLut_t constLut = initializeConstLut(); 

AFAIK這部作品在某種意義上說,constLut被corretly初始化,但有一個內存泄漏,因爲bigLut_t * LUT被永遠不會釋放。我嘗試使用智能指針,但導致constLut中的值非常隨機。我對通過嘗試谷歌解決方案所發現的信息缺乏感到困惑。

回答

2

你是如何使用shared_ptr的?請嘗試以下操作:

// test.h 
#pragma once 
#include <boost/array.hpp> 

typedef boost::array<int,100000> bigLut_t; 
extern const bigLut_t constLut; 

// test.cpp 
#include <fstream> 
#include <boost/filesystem.hpp> 
#include "test.h" 

boost::shared_ptr<bigLut_t> initializeConstLut() 
{ 
    if(boost::filesystem::exists("my_binary_file") == false) { 
     std::ofstream outStream("my_binary_file", ios::out | ios::binary); 
     bigLut_t* tempLut = new bigLut_t; 
     for(int i = 0; i < 100000; ++i) { 
      // Imagine this taking a long time, 
      // which is why we're using a file in the first place 
      tempLut->at(i) = i; 
     } 
     outStream.write(reinterpret_cast<char*>(tempLut), sizeof(bigLut_t)); 
     outStream.close(); 
     delete tempLut; 
    } 
    // We can't write "bigLut_t lut;" because that would cause a stack overflow 
    boost::shared_ptr<bigLut_t> lut(new bigLut_t); // lut gets never deallocated 
    std::ifstream inStream("my_binary_file", ios::in | ios::binary); 
    inStream.read(reinterpret_cast<char*>(lut), sizeof(bigLut_t)); 
    inStream.close(); 
    return lut; 
} 

const bigLut_t constLut = *(initializeConstLut().get()); 
+0

我很確定我確實嘗試了auto_ptr和shared_ptr,但我猜測它沒有工作,因爲我在initializeConstLut函數中取消了引用智能指針。但是,這似乎是完美的,謝謝。儘管你錯過了get()到lut的右側。 – zeroes00 2010-11-27 08:59:18

+0

我不想冒犯巴黎同胞,但我覺得很可惜,你的解決方案需要建造一個臨時性的巨大物體,然後是複製結構(我認爲這是昂貴的),並且臨時銷燬臨時的 – icecrime 2010-11-27 09:06:08

0

如果唯一的泄漏是在應用程序的使用壽命結束時,有一種說法認爲已知物體不會泄漏。如果你對該對象所做的一切都是從內存中刪除它(並且不釋放其他資源,也不更新像數據庫和文件這樣的外部內容),那麼你可以把它留在那裏。畢竟,通過明確地從內存中刪除它們,你只是阻止了應用程序的完成而沒有真正的好處:內存將以某種方式被釋放。

+0

我猜你沒注意到的數據(這是在堆)由LUT指出被複制constLut和LUT以來從未釋放,大的查找表留在那裏無用的堆消耗內存 – zeroes00 2010-11-27 08:47:37

+0

Blimey,沒錯!哦,好吧... – Dialecticus 2010-11-27 21:34:17

0

添加一個明確的破壞lut。你有責任摧毀你創造的任何東西。最好是明確地做到這一點:

void destroyLut(bigLut_t& lut) 
    { 
     delete &lut; 
    } 

然後簡單地退出main之前調用這個函數。你不應該依賴C++中的靜態初始化和破壞。更好的是始終做顯式的初始化和破壞。

1

那豈不是更容易只是讓你的全球對象的指針,new臨時對象從initializeConstLut和退出函數之前設置全局指針指向的對象?

如果對象太大,您應該避免涉及複製和/或分配的任何解決方案。所以,你留下了幾個選擇:

  • 使全局對象的指針(免費副本)
  • 使全局對象非const,並直接在它讀
  • 我不能相信我這樣說,但使用一個單例如模式幷包裝對象。這將允許你隔離初始化並提供訪問的公共方法,這將返回一個對數組的const引用。

我沒有在這裏添加太多的細節,因爲它真的取決於您的要求(我在想線程安全)。

1

只是做的老式方法是好的 - 創建一個自動指針的生命週期是整個應用程序生命週期,以及一個外部參考指針:

// test.h 
#pragma once 
#include <boost/array.hpp> 

typedef boost::array<int,100000> bigLut_t; 
extern const bigLut_t& constLut; // make it a reference 

// test.cpp 
#include <fstream> 
#include <boost/filesystem.hpp> 
#include "test.h" 

namespace { 
    std::auto_ptr<bigLut_t> initializeConstLut() 
    { 
     std::auto_ptr<bigLut_t> lut(new bigLut_t); 

     if(boost::filesystem::exists("my_binary_file") == false) { 
      std::ofstream outStream("my_binary_file", ios::out | ios::binary); 

      for(int i = 0; i < 100000; ++i) { 
       // Imagine this taking a long time, 
       // which is why we're using a file in the first place 
       lut->at(i) = i; 
      } 

      outStream.write(reinterpret_cast<char*>(lut), sizeof(bigLut_t)); 
      outStream.close(); 

      // no point writing then reading the same data 
     } else {    
      std::ifstream inStream("my_binary_file", ios::in | ios::binary); 
      inStream.read(reinterpret_cast<char*>(lut.get()), sizeof(bigLut_t)); 
      inStream.close(); 
     } 

     return lut; 
    } 

    // local to this compilation unit, deletes object on exit 
    std::auto_ptr<bigLut_t> constLutPtr (initializeConstLut()); 
} 

// the extern reference refers to the object held by the auto_ptr 
const bigLut_t& constLut (*constLutPtr.get()); 

沒有額外的複製,客戶端代碼見extern變量與前面一樣,儘管鏈接器可能必須具有額外的間接尋址而不是固定地址中的外部變量(& constLutPtr位於堆上而不是靜態數據區中)。

如果constLut的固定地址很重要,請返回到具有extern值而不是extern引用,使用reinterpret cast和const_cast的const_cast讀取數據爲const32。通過reinterpret_cast<char*>(const_cast<bigLut_t*>(&constLut))來讀取流。

2

有幾個很好的解決你的問題。到目前爲止提供的解決方案並不是很好的解決方案(特別是動態分配和複製,並且依賴臨時對象的生命週期只是非常糟糕的™)。我會給你一個通用的解決方案。

一個簡單的方法提供了巨大的常數是使用邁爾斯單,這意味着限定常數函數中的static局部變量,返回對它的引用:

inline BigThing const& theBigThing() 
{ 
    static BigThing const theInstance; // Default constructor does the init job. 
    return theInstance; 
} 

這還不是一個完整的解決方案,但讓我們先來看看如何擺脫需要調用的函數,而不是直接與負責什麼,看起來樣一個恆定的:

namespace detail { 
    inline BigThing const& theBigThing() 
    { 
     static BigThing const theInstance; // Default constructor does the init job. 
     return theInstance; 
    } 
} 

BigThing const& theBigThing = detail::theBigThing(); // No copying, just ref. 

在你的情況下BigThing是應該從文件中的數據初始化的數組,不能直接依賴默認的構造函數。你可以,如果你定義了一個包裝類,這是一種方法。好吧,讓我們做到這一點(這就是我會選擇,我認爲):

namespace detail { 

    struct BigThingWrapper 
    { 
     BigThing thingy_; 

     BigThingWrapper() 
     { 
      // Initialize the thingy_ member here. 
     } 
    }; 

    inline BigThing const& theBigThing() 
    { 
     static BigThingWrapper const theInstance; 
     return theInstance.thingy_; 
    } 
} 

BigThing const& theBigThing = detail::theBigThing(); // No copying, just ref. 

注1:我用inline使代碼令人信服可以被放置在一個頭文件。只需移除實施文件中的實施即可。注2:此代碼未經編譯器手動操作,因此可能包含ERORS,TYPPOS和其他內容。 :-)但這是你的答案。注意3:如前所述,還有其他好的方法可以做到這一點,所以這不是「答案」,也沒有「答案」,但答案是「一個」。

乾杯&心連心,