将字节数组转换为std数据类型

时间:2014-11-28 13:10:15

标签: c++ pointers memory-management casting

嗯,嗨。我正在尝试编写一个自定义堆栈内存分配器来帮助我的游戏编程,并遇到了一个问题。所以,假设我的分配器有一个char *缓冲区,我想为int获取一些内存:

class MemoryStack
{
public:
MemoryStack(unsigned size)
{
mSize=size;
mTop=0;
mBuffer = new(std::nothrow) char [size];
}

char* allocate(unsigned size)
{
if (mTop + size > mSize)
return nullptr;
char* out = mBuffer+mTop;
mTop+=size;
return out;
}

private:
char* mBuffer;
unsigned mTop;
unsigned mSize;
};

int main ()
{
MemoryStack stack(1024);
int testval = 6;
int* ptr = (int*)stack.allocate(sizeof(int));
*ptr = testval;
std::cout<<*ptr;
}

现在,这个工作,然后打印出6.然而,当我尝试类似的东西时:

int main ()
{
MemoryStack stack(1024);
std::string str = "HELLO :p";
std::string* strptr = (std::string*)stack.allocate(sizeof(std::string));
*strptr = str ;
std::cout<<*strptr;
}

...这给了我一个Bad Ptr问题和一个分段错误,导致程序崩溃。谁能解释为什么会这样?这可能是因为某些运算符=重载?有没有办法安全地处理这个?谢谢!

修改 以下代码是我收到的帮助之后的最新实现 - 它没有经过全面测试,但似乎按预期工作。如果发现任何错误,我会自然地修改它,但是为了帮助任何感兴趣的人,这里是^ _ ^如果你愿意,可以随意使用它,虽然它几乎不是计算机科学的顶峰。

class MemoryStack;

/**Serves as a bookmark for the memory stack in order to allow to clear only part of the memory.*/
class MemoryBookmark
{
private:

    /**Private constructor may only be called by the memory stack object.*/
    MemoryBookmark(unsigned value)
    {
        mBookmark = value;
    }

    unsigned mBookmark;

public:
    friend class MemoryStack;

    /**Returns the index of the position that will be the new stack top pointer.*/
    decltype(mBookmark) getValue() const
    {
        return mBookmark;
    }
};

/**Acts as a basic memory stack to help reduce allocation costs, as well as add to the fun! Use with care, as destructors must be called manually.*/
class MemoryStack
{
private:
    char* mBuffer;
    size_t mTop;
    size_t mCapacity;
    size_t mAlignment;

public:

    /**Initialises the class, reserving _capacity_ bytes for use. It can not be resized for efficiency purposes.*/
    MemoryStack(unsigned capacity)
    {
        mCapacity = capacity;
        mTop = 0;
        mBuffer = new(std::nothrow) char[capacity];
        mAlignment = 4;
    }

    /**Frees the memory, invalidating all internal memory. Doesn't call destructors.*/
    ~MemoryStack()
    {
        if (mBuffer)
            delete[] mBuffer;
    }

    /**Creates an instance of the given type with Args if possible, using aligned internal memory.*/
    template <typename T, typename... Args>
    void create(T*& ptr, Args&&... args)
    {
        ptr = (T*)allocate(sizeof(T));
        if (!ptr)
            return;
        else
            new (ptr)T(std::forward<Args>(args)...);
    }

    /**Calls the destructor of the pointer. Must be used if destruction important.*/
    template<typename T>
    void destroy(T* ptr)
    {
        ptr->~T();
    }

    /**Allocates a piece of memory for use.*/
    void* allocate(size_t amount)
    {
        size_t bt = (size_t)(mBuffer + mTop);
        size_t alignoffset = mAlignment - (bt & (mAlignment - 1));
        alignoffset = alignoffset == mAlignment ? 0 : alignoffset;
        size_t size = amount + alignoffset;
        if (size + mTop > mCapacity)
            return nullptr;
        else
        {
            mTop += size;
            return (void*)(bt + alignoffset);
        }
    }

    /**Returns the amount of memory used.*/
    size_t size() const
    {
        return mTop;
    }

    /**Returns the size of the memory reserved for use.*/
    size_t capacity() const
    {
        return mCapacity;
    }

    /**Returns the number of bytes remaining for allocation.*/
    size_t remaining() const
    {
        return mCapacity - mTop;
    }

    /**Checks whether the internal memory was allocated successfully.*/
    bool isValid() const
    {
        return mBuffer != nullptr;
    }

    /**Creates a 'bookmark' which can be used to clear the stack until a given point.*/
    MemoryBookmark createBookmark() const
    {
        return MemoryBookmark(mTop);
    }

    /**Resets the stack. All data inside may now be overwritten. Doesn't call destructors.*/
    void reset()
    {
    mTop = 0;
    }

    /**Resets the stack up to a given bookmark. Again, no destructors called!*/
    void resetToBookmark(const MemoryBookmark bookmark)
    {
        mTop = bookmark.getValue();
    }

    /**Sets the alignment of the reservations in memory.*/
    void setAlignment(size_t alignment)
    {
        mAlignment = alignment;
    }

    /**Returns the currently used alignment.*/
    decltype(mAlignment) getAlignment() const
    {
        return mAlignment;
    }
};

/**Test class.*/
class Test
{
public:
    Test(int val)
    {
        v = val;
        std::cerr << "Constructor\n";
    }

    ~Test()
    {
        std::cerr << "Destructor";
    }

    int v;
};

/**Test it! XD*/
int main()
{
    using namespace std;
    {
        MemoryStack stack(4096);

        Test* test=nullptr;
        int* i1, *i2;
        char* c1, *c2;
        stack.create(test,3);
        stack.create(i1, 2);
        stack.create(c1, 'a');
        stack.create(i2, 3);
        stack.create(c2, 'm');
        stack.destroy(test);
        stack.reset();
    }

    cin.get();
}

2 个答案:

答案 0 :(得分:1)

未初始化的内存区域不是std::string。你必须在一个区域中构建一个对象,其位置为new,如下所示:

std::string *strptr = (std::string*) stack.allocate(sizeof(std::string));
new (strptr) std::string;

值得考虑在中心位置执行此操作,例如MemoryStack中的成员函数模板。

修改

我之前忘记提到这一点,这很遗憾,因为血腥重要:

如果使用placement new构造对象,则还必须手动对其进行破坏。他们不会自己做,因为他们没有自动存储持续时间,你不能使用delete,因为内存没有分配new。语法非常简单:

strptr->~string();

将这两部分作为MemoryStack的一部分是个好主意,例如:{/ p>

class MemoryStack {
  ...

  template<typename T, typename... Args>
  T *create(Args&&... args) {
    T *ptr = allocate(sizeof(T));
    try {
      new(ptr) T(std::forward<Args>(args)...);
    } catch(...) {
      deallocate(ptr);
      throw;
    }

    return ptr;
  }

  template<typename T>
  void destroy(T *ptr) {
    ptr->~T();
    deallocate(ptr);
  }

  ...
};

稍后写

std::string *strptr = stack.create<std::string>("foo");

...

stack.destroy(strptr);

左右。专业提示:为自己构建一个删除器,您可以将其与std::unique_ptrstd::shared_ptr结合使用,以简化异常安全。

答案 1 :(得分:0)

您正在尝试分配std :: string的大小 std :: string * strptr =(std :: string *)stack.allocate(sizeof(std :: string));

你可能意味着什么 std :: string * strptr =(std :: string *)stack.allocate(str.size()));

Wintermute是一个不错的主意,但它可能不会做你想要的 一个。你的分配函数将返回nullptr 湾“new”将分配一个新的内存区域,而不是在你的“堆”

可能不会崩溃......

Draknghar :(我要年轻时添加评论) 改变主意。它会工作。但是你没有设置mTop ...... 这样做似乎有些不对劲。你已经拥有阵列并试图在其中分配新内存?为什么?只需将你的字符串复制到其中,然后设置mTop。