初始化一个大的全局常量对象

时间:2010-11-27 08:21:36

标签: c++ initialization const global

如何使用文件初始化一个太大而无法在堆栈中创建的全局const对象?这是我到目前为止的尝试:

// 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得到了相应的初始化,但由于bigLut_t * lut从未被释放,因此存在内存泄漏。我尝试使用智能指针,但这导致constLut中的值非常随机。通过尝试谷歌解决方案,我发现缺乏信息让我感到困惑。

6 个答案:

答案 0 :(得分: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());

答案 1 :(得分:2)

您的问题有几个很好的解决方案。到目前为止作为答案提供的解决方案并不是很好的解决方案(特别是,动态分配和复制以及依赖临时对象的生命周期只是非常糟糕)。我只会给你一个共同的解决方案。

提供巨大常量的一种简单方法是使用 Meyers'单例,这意味着将常量定义为函数中的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:如上所述,还有其他好方法可以做到这一点,所以这不是“答案”,也没有“答案”,但这是“答案”。

干杯&amp;第h。,

答案 2 :(得分:1)

将全局对象作为指针new来自initializeConstLut的临时对象并在退出函数之前设置指向该对象的全局指针会不会更容易?

如果对象太大,则应避免任何涉及复制和/或分配的解决方案。所以你有几个选择:

  • 使全局对象成为指针(免费副本)
  • 使全局对象成为非const并直接在其中读取
  • 我不敢相信我这么说,但是使用 Singleton like 模式并包装对象。这将允许您隔离初始化并提供一个公共访问方法,该方法将返回对数组的const引用。

我在这里没有添加太多细节,因为它实际上取决于您的要求(例如我在考虑线程安全性)。

答案 3 :(得分: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变量,尽管链接器可能必须有一个额外的间接而不是extern varibale在固定地址中(&amp; constLutPtr在堆上而不是在静态数据区中) )。

如果constLut的固定地址很重要,请返回使用extern值而不是extern引用,使用重新解释转换和const&amp; constLutPtr的const_cast读取数据。将reinterpret_cast<char*>(const_cast<bigLut_t*>(&constLut))传递给流读取。

答案 4 :(得分:0)

如果唯一的泄漏是在应用寿命结束时,有一种思路认为已知物体不会泄漏。如果您对该对象所做的只是将其从内存中删除(并且不释放其他资源,也不更新某些外部内容,如数据库和文件),那么您可以将其保留在那里。毕竟,通过从内存中明确地删除它们,你只是阻止应用程序完成没有真正的好处:内存将以某种方式释放。

答案 5 :(得分:0)

添加lut的显式破坏。您有责任销毁您创建的任何内容。最好是明确地做到这一点:

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

然后在退出main之前调用此函数。你永远不应该依赖C ++中的静态初始化和销毁​​。最好是始终进行明确的初始化和破坏。