3

有没有办法定义一个宏(或类似的东西),允许对象在堆栈或堆上干净地分配?

例如。当前代码:

A a;
a.someFunc();

最简单的建议可能如下,但正如您在下面看到的,维护 2 组代码并不是很干净。

#ifdef USE_STACK
  A a;
  a.someFunc();
#elseif USE_HEAP
  A* a = new A();
  a->someFunc();
#endif

我正在寻找可用于编译代码的设计模式/代理类,具体取决于客户的需求。

编辑:该代码用于为嵌入式设备/(嵌入式)Linux / Windows Mobile 构建库。大多数客户只想要基于堆栈的分配。其他一些人要求用堆栈换堆。

谢谢,查尔斯

4

5 回答 5

5

编辑:改进以允许通过调用包装的成员函数operator->

扩展 Manuel 的答案以使其更完整,试试这个:

#include <iostream>

#define USE_STACK

template <class T>
class HeapWrapper {
#ifdef USE_STACK
    T obj_;
#else
    T *obj_;
#endif
public:
#ifdef USE_STACK
    HeapWrapper() : obj_() {}

    template <class A1>
    HeapWrapper(const A1 &a1) : obj_(a1) {}

    template <class A1, class A2>
    HeapWrapper(const A1 &a1, const A2 &a2) : obj_(a1, a2) {}

    // etc

#else
    HeapWrapper() : obj_(new T()) {}
    ~HeapWrapper() { delete obj_; }

    template <class A1>
    HeapWrapper(const A1 &a1) : obj_(new T(a1)) {}

    template <class A1, class A2>
    HeapWrapper(const A1 &a1, const A2 &a2) : obj_(new T(a1, a2)) {}

    // etc
#endif

#ifdef USE_STACK
    operator const T &() const    { return obj_; }
    operator T &()                { return obj_; }
    T *operator->()               { return &obj_; }
    T& operator*()                { return obj_; }
#else
    operator const T &() const    { return *obj_; }
    operator T &()                { return *obj_; }
    T *operator->()               { return obj_; }
    T& operator*()                { return *obj_; }
#endif

    // cast operators makes this work nicely
    HeapWrapper &operator=(const T &rhs) { *obj_ = rhs; return *this;}
};


class A {
public:
    void member(int x) {
        std::cout << x << std::endl;
    }
};


int main() {
    HeapWrapper<int> x1(5);
    HeapWrapper<int> x2;
    HeapWrapper<int> x3 = x1;
    HeapWrapper<int> x4 = 3;

    std::cout << x1 << " " << x2 << " " << x3 << " " << x4 << std::endl;

    // example using a custom class's members..
    HeapWrapper<A> a1;
    a1->member(5);
}
于 2009-04-09T22:25:40.000 回答
2

这样的事情可能会有所帮助:

template <typename T>
class HeapWrapper
{
#ifdef USE_STACK
  T obj_;
#else
  T *obj_;
#endif
public:
#ifdef USE_STACK
  HeapWrapper() : obj_() {}
#else
  HeapWrapper() : obj_(new T()) {}
#endif

#ifdef USE_STACK
  const T& obj() const
  { return obj_; }

  T& obj() const
  { return obj_; }
#else
  const T& obj() const
  { return *obj_; }

  T& obj() const
  { return *obj_; }
#endif
};

但是,请注意,这要么将您限制为仅具有默认构造函数的对象。包装类可以提供一个函数,该函数可以由包装类的可变参数模板函数Init(...)转发(或者只需为您需要的每个参数添加一个):template <typename T1, template T2, [etc]> Init(const T1 &x1, cons tT2 &x2)

template <typename T1>
void Init(const T1& x1)
{
#ifdef USE_STACK
  obj_.Init(x1);
#else
  obj_->Init(x1);
#endif
}

template <typename T1, typename T2>
void Init(const T1& x1, const T2& x2)
{
#ifdef USE_STACK
  obj_.Init(x1, x2);
#else
  obj_->Init(x1, x2);
#endif
}

如果您的编译器已经有可变参数模板,则加分:

template<typename... T>
void foo(const T&... values) {
#ifdef USE_STACK
  obj_.Init(values...);
#else
  obj_->Init(values...);
#endif
}
于 2009-04-09T22:06:40.093 回答
2

如果您问这个问题,我认为您应该开始研究自定义分配器。对于初学者,如果您分配一大块内存,您可以通过更改分配器内部结构来使用它,就像堆栈或堆一样。您将使用智能指针分配所有对象(以防止内存泄漏),并且所有内存都将从您的自定义堆中分发给您。

这允许您根据客户的需求调整分配实现,同时如果以这种方式调整,还可以为您提供基于堆栈的分配器的性能。

您的代码将始终使用“堆”样式的对象创建,因此您只需要一种编码方式,并且不需要条件宏。检查块分配器(创建几个固定大小的块堆,将下一个大小的第一个空闲块提供给调用者(例如块的大小为 16 字节、32 字节、64 字节等),它的速度非常快allocate 和 free,尽管它在内存使用方面效率低下)。

于 2009-04-09T22:17:06.480 回答
1

通常堆栈的大小是有限的,因此无论如何都需要在堆上分配任何大对象。

所有实现 RAII 保护习语的实例(例如,获取和释放互斥锁)都需要放在堆栈上,以便在您离开上下文(例如通过返回或异常)时清理它们

让您切换的习语通常不是很有用,因为需要谨慎决定是否将对象放置在堆栈或堆上。通常,其中一个会更好地解决问题,然后切换就没有意义了。

于 2009-04-09T22:06:18.627 回答
0

不是最终答案,但我希望它可以提供帮助:

#ifdef USE_STACK
  A a;
#elseif USE_HEAP
  auto_ptr<A> CAUTION_USE_OF_AUTOPTR_a( new A() );
  A& a = CAUTION_USE_OF_AUTOPTR_a.get();
#endif
a.someFunc();
于 2009-04-09T22:17:34.217 回答