9

我想在下面的代码中将 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;
}

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

4

5 回答 5

13

如果您不介意使用总 hack,您可以尝试放置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函数在不释放内存的情况下调用析构函数,这在使用带有智能指针的自动内存时是必需的。可以在此处找到代码演示。

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

  MyClass c = something ? MyClass(1) : MyClass("string");
  c.doStuff();
于 2013-08-20T23:23:54.630 回答
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();

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

一般来说,建议使用智能指针而不是自己进行内存管理。这在处理可能引发异常的代码时特别有用。

于 2013-08-20T23:23:45.340 回答
5

Benjamin Bannier 的建议在 GCC 4.7.2 上对我有用,没有特殊的编译器标志(即默认优化),或者使用-O0, -O1, -O2, or -O3

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

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

  c.doStuff();

  return 0;
}

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

于 2013-08-20T23:42:33.057 回答
2

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

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

我试过了,在我的例子中,我只看到一个对象被构造和销毁。

于 2013-08-21T00:18:22.107 回答
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;
};

然后您可以轻松地执行以下操作,只需一次初始化您的对象,而无需使用任何类型的 hack:

  MyClass c;
  if (something) {
      c.init(1);
  } else {
      c.init("string");
  }
  c.doStuff();
于 2013-08-21T18:11:03.510 回答