在不调用构造函数的情况下在堆栈上分配内存

时间:2013-08-20 23:10:56

标签: c++

我想在下面的代码中将MyClass保存在堆栈内存中(更简单,更快),但是避免调用默认构造函数:

#include <iostream>

class MyClass {
public:
  MyClass() {
    std::cout << "MyClass()" << std::endl;
  }

  MyClass(int a) {
    std::cout << "MyClass(" << a << ")" << std::endl;
  }

  MyClass(const std::string& a) {
    std::cout << "MyClass(\"" << a << "\")" << std::endl;
  }

  void doStuff() {
    std::cout << "doStuff()" << std::endl;
  }
};

int main(int argc, char* argv[]) {
  bool something;
  if (argc > 1)
    something = 1;
  else
    something = 0;

  MyClass c;
  if (something)
    c = MyClass(1);
  else
    c = MyClass("string");
  c.doStuff();

  return 0;
}

据我所知,避免调用默认构造函数的唯一方法是使用指针,但是我必须在堆中分配并处理内存管理。还有其他办法吗?

5 个答案:

答案 0 :(得分:13)

如果您不介意使用完全黑客攻击,则可以尝试展示位置new

char mem[sizeof(MyClass)] alignas(MyClass);
auto custom_deleter = [](MyClass *p){ p->~MyClass(); };
std::shared_ptr<MyClass> c(new (mem) MyClass, custom_deleter);

使用alignas确保为您的对象正确对齐分配的自动内存。 custom_deleter函数调用析构函数而不释放内存,这在使用具有智能指针的自动内存时是必需的。可以找到代码演示here

但是,对于您的问题,存在更优雅的解决方案。您可以改为使用复制构造函数:

  MyClass c = something ? MyClass(1) : MyClass("string");
  c.doStuff();

答案 1 :(得分:5)

你是对的。您不可能避免调用默认构造函数,除非您想复制一些代码,如下所示。

if (something) {
    MyClass c(1);
    c.doStuff();
}
else {
    MyClass c("string");
    c.doStuff();
}

我建议你创建堆的对象,但是将内存处理委托给另一个类。使用C ++ 03,可以使用std::auto_ptr类。使用C ++ 11时,不推荐使用auto_ptr,而是可以使用shared_ptrunique_ptr

以下是使用shared_ptr -

的一些示例代码
std::shared_ptr<MyClass> c;
if (something)
    c.reset(new MyClass(1));
else 
    c.reset(new MyClass("string"));

c->doStuff();

当对象超出范围时,将自动删除该对象。

通常,建议使用智能指针而不是自己进行内存管理。当您处理可能抛出异常的代码时,这尤其有用。

答案 2 :(得分:5)

Benjamin Bannier的建议适用于GCC 4.7.2,没有特殊的编译器标志(即默认优化),或者-O0-O1-O2或{{1 }}:

-O3

当我在GCC 3.4.4(大约2004年),GCC 3.4.6(2006年),GCC 4.2.4(2007年)和GCC 4.7.2(2012年)上尝试时,我得到了相同的结果。

答案 3 :(得分:2)

尝试,由于编译器复制省略,这可能会避免额外的复制:

MyClass makeInstance(int a, string& b) {
    if (something) {
        return MyClass(a);
    } else {
        return MyClass(b);
    }
}

我试过了,在我的情况下,我看到只有一个对象被构建和销毁。

答案 4 :(得分:0)

只有一个很好的解决方案可以避免任何不必要的构造函数调用:延迟初始化。只有一个构造函数,它只是让您的对象进入已定义的状态,并在init()方法中进行实际初始化。像这样:

class MyClass {
public:
    MyClass() : initialized(false) { std::cout << "MyClass()" << std::endl; };

    void init(int a) {
        std::cout << "MyClass(" << a << ")" << std::endl;
        initialized = true;
    };

    void init(const std::string& a) {
        std::cout << "MyClass(\"" << a << "\")" << std::endl;
        initialized = true;
    };

    void doStuff() {
        assert(initialized);
        std::cout << "doStuff()" << std::endl;
    };

private:
    bool initialized;
};

然后你可以轻松地执行以下操作,在不使用任何类型的黑客攻击的情况下初始化对象一次:

  MyClass c;
  if (something) {
      c.init(1);
  } else {
      c.init("string");
  }
  c.doStuff();