选择两个构造函数

时间:2013-01-18 18:05:06

标签: c++ constructor

问题:我有一个带有两个构造函数的不可复制对象。我需要使用其中一个构造函数创建一个对象,然后在一些常用代码中使用它: -

使用可复制对象,它看起来像这样,并且很简单:

Object a;

if (condition) 
   a = Object(p1);
else
   a = Object(p2,p3,p4);

a.doSomething();

但是,该对象是不可复制的,所以我必须这样做:

boost::scoped_ptr<Object> a;

if (condition) 
   a = new Object(p1);
else
   a = new Object(p2,p3,p4);

a->doSomething();

这感觉太复杂了。有没有更好的解决方案?

7 个答案:

答案 0 :(得分:4)

这是一个非常可怕的黑客攻击,假设Object是默认构造的:

Object a;
a.~Object();

if (condition) { ::new (&a) Object(p1); }
else           { ::new (&a) Object(p2, p3, p4); }

Don't use this.

另一种选择是使用union,但你也需要在该设置中手动调用析构函数。


使用Boost.Optional(使用in-place factories)可以实现更清洁的解决方案。 (感谢@ K-Ballo挖掘细节!)

#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>

struct Object
{
    explicit Object(int) {}
    explicit Object(int, float, std::string) {}

    Object(Object const &)             = delete;
    Object(Object &&)                  = delete;
    Object & operator=(Object const &) = delete;
    Object & operator=(Object &&)      = delete;
};

boost::optional<Object> a;

if (condition) { a = boost::in_place(0); }
else           { a = boost::in_place(0, 1.0f, "two" ); }

答案 1 :(得分:3)

对我而言看起来非常合理。它清晰,简单,相对简洁。

答案 2 :(得分:1)

我没有看到复杂性 ...如果你需要根据if条件有效地构建声明指针并使用new是你唯一的选择。您不一定需要做的是:

  1. 使用scoped_ptr(通常是个好主意)
  2. 在“主”代码中的if中包含构造函数。你的是工厂的典型用例(参见例如http://en.wikipedia.org/wiki/Factory_method_pattern)。
  3. 编辑:在第二句中加入“有效”。

答案 3 :(得分:1)

auto const doSomethingTo = []( Object&& o ) { o.doSomething(); };
doSomethingTo( condition? Object( p1 ) : Object( p1, p2, p3 ) );

免责声明:编译器未触及的代码。


编辑:当Object( Object&& )构造函数为private时,上面的代码无法使用MSVC 11.0进行编译(是的,甚至是去年的11月CTP),但编译正常使用MinGW g ++ 4.7.1和某些版本的clang

似乎it should compile

所以,它可能是Visual C ++中的一个错误 - 但遗憾的是我没有找到一个简单的解决方法。


假定为Visual C ++编译器错误的不安解决方法:

#include <fstream>
#include <iostream>
using namespace std;

class Object
{
private:
    Object( Object const& );
    Object( Object&& );
public:
    void doSomething() const {}
    Object( int ) {}
    Object( int, int, int ) {}
};

int main( int argc, char* argv[] )
{
    int p1 = 0, p2 = 0, p3 = 0;
    bool condition = argc == 2;

    auto const doSomething1 = [=]() { Object o( p1 ); o.doSomething(); };
    auto const doSomething2 = [=]() { Object o( p1, p2, p3 ); o.doSomething(); };

    if( condition ) { doSomething1(); } else { doSomething2(); }
}

另一个答案认为new(阅读:动态分配)是您唯一的选择。

那是错的。

答案 4 :(得分:1)

你的解决方案确实没有问题,尽管如此 其他人已经提过,如果你这样会更容易理解 使用条件运算符而不是if。但你应该 考虑重构的可能性。如果你考虑所有 将对象用于单独函数的代码 (通过引用获取对象),然后是:

if ( condition ) {
    Object a( p1 );
    doWhatever( a );
} else {
    Object a( p2, p3, p4 );
    doWhatever( a );
}

可能更好(或者不是 - 我认为没有 关于在这两者之间进行选择的“正确”答案。)

答案 5 :(得分:0)

我认为您的代码没问题。

您可能想要考虑条件运算符 ? :

boost::scoped_ptr<Object> a( condition ? new Object(p1) : new Object(p2,p3,p4) );
a->doSomething();

答案 6 :(得分:0)

所以,这是一个快速的技巧,使这项工作,而无需手动构建对象。相反,我创建了一个Deferred<T>模板,该模板表示自动存储中的对象,其构造被延迟(并且可能永远不会发生)。

buff中的Deferred应该替换为union,因为这会处理对齐问题(假设您有C ++ 11支持的功能)。当你致电constructed时,get()为真的断言可能是一个好主意。

构建对象后,您可以隐式将Deferred<T>强制转换为T&,然后将T&用作延迟构建的T的别名。

理论上,你可以取消constructed bool,如果你可以证明它总会被构建,但我会反对它。除此之外,这应该几乎和你可以实现的效率一样高效。对于C ++ 11 union,它甚至可能符合标准。

哦,是的,它应该通过完美转发来增强。

#include <utility>

// does not handle alignment issues:
template<typename T>
struct Deferred {
   Deferred():constructed(false) {}
  operator T&() { return get(); }
  T& get() { return *reinterpret_cast<T*>(&buff[0]); }
  template<typename... Args>
  T& construct( Args... args ) {
    new(&buff[0]) T(args...);
    constructed = true;
    return get();
  }
  ~Deferred() {
    if (constructed) {
      get().~T();
    }
  }
private:
  bool constructed;
  char buff[sizeof(T)];
};

#include <iostream>

struct Object {
  bool is_int;
  Object( int x ):is_int(true) {}
  Object( double d ):is_int(false) {}
  ~Object() {
    std::cout << "~Object("<<is_int<<") destroyed\n";
  }
};

enum which_test {
  as_int,
  as_double,
  do_not,
};
void test(which_test v) {
  std::cout << v << "\n";
  Deferred<Object> o;
  if(v==as_int) {
    o.construct( 7 );
  } else if (v==as_double) {
    o.construct( 7.0 );
  } else {
  }
}

int main() {
  test(as_int);
  test(as_double);
  test(do_not);
}
相关问题