在c++中定义对象而不调用其构造函数

Defining an object without calling its constructor in C++

本文关键字:调用 构造函数 对象 c++ 定义      更新时间:2023-10-16

在c++中,我希望像这样将对象定义为类的成员:

Object myObject;

然而,这样做将尝试调用它的无参数构造函数,该构造函数不存在。但是,我需要在包含类完成一些初始化之后调用构造函数。像这样。

class Program
{
public:
   Object myObject; //Should not try to call the constructor or do any initializing
   Program()
   {
      ...
      //Now call the constructor
      myObject = Object(...);
   }
}

存储指向Object而不是实际Object的指针

:

class Program
{
public:
   Object* myObject; // Will not try to call the constructor or do any initializing
   Program()
   {
      //Do initialization
      myObject = new Object(...);  // Initialised now
   }
}

别忘了在析构函数中delete它。现代c++在这方面提供了帮助,您可以使用auto_ptr shared_ptr而不是原始内存指针。

其他人已经发布了使用原始指针的解决方案,但智能指针将是一个更好的主意:

class MyClass {
  std::unique_ptr<Object> pObj;
  // use boost::scoped_ptr for older compilers; std::unique_ptr is a C++0x feature
public:
  MyClass() {
    // ...
    pObj.reset(new Object(...));
    pObj->foo();
  }
  // Don't need a destructor
};

这避免了添加析构函数的需要,并且隐式地禁止复制(除非您自己编写operator=MyClass(const MyClass &))。

如果您想避免单独的堆分配,可以使用boost的aligned_storage和placement new来完成。测试:

template<typename T>
class DelayedAlloc : boost::noncopyable {
  boost::aligned_storage<sizeof(T)> storage;
  bool valid;
public:
  T &get() { assert(valid); return *(T *)storage.address(); }
  const T &get() const { assert(valid); return *(const T *)storage.address(); }
  DelayedAlloc() { valid = false; }
  // Note: Variadic templates require C++0x support
  template<typename Args...>
  void construct(Args&&... args)
  {
    assert(!valid);
    new(storage.address()) T(std::forward<Args>(args)...);
    valid = true;
  }
  void destruct() {
    assert(valid);
    valid = false;
    get().~T();
  }
  ~DelayedAlloc() { if (valid) destruct(); }
};
class MyClass {
  DelayedAlloc<Object> obj;
public:
  MyClass() {
    // ...
    obj.construct(...);
    obj.get().foo();
  }
}
或者,如果Object是可复制的(或可移动的),您可以使用boost::optional:
class MyClass {
  boost::optional<Object> obj;
public:
  MyClass() {
    // ...
    obj = Object(...);
    obj->foo();
  }
};

您可以通过这个技巧完全控制对象的构造和销毁:

template<typename T>
struct DefferedObject
{
    DefferedObject(){}
    ~DefferedObject(){ value.~T(); }
    template<typename...TArgs>
    void Construct(TArgs&&...args)
    {
        new (&value) T(std::forward<TArgs>(args)...);
    }
public:
    union
    {
        T value;
    };
};

应用于您的样品:

class Program
{
public:
   DefferedObject<Object> myObject; //Should not try to call the constructor or do any initializing
   Program()
   {
      ...
      //Now call the constructor
      myObject.Construct(....);
   }
}

这个解决方案的最大优点是,它不需要任何额外的分配,并且对象内存分配正常,但是您可以控制何时调用构造函数。

另一个示例链接

如果您可以访问boost,则提供了一个方便的对象,称为boost::optional<> -这避免了动态分配的需要,例如

class foo
{
  foo()  // default std::string ctor is not called..
  {
    bar = boost::in_place<std::string>("foo"); // using in place construction (avoid temporary)
  }
private:
  boost::optional<std::string> bar;
};

如果您可以将其他初始化移到构造函数中,您也可以重写代码以使用构造函数初始化列表:

class MyClass
  {
    MyObject myObject; // MyObject doesn't have a default constructor
  public:
    MyClass()
      : /* Make sure that any other initialization needed goes before myObject in other initializers*/
      , myObject(/*non-default parameters go here*/)
      {
      ...
      }
  };

您需要意识到,遵循这样的模式将导致您在构造函数中进行大量工作,这反过来又导致需要掌握异常处理和安全性(因为从构造函数返回错误的规范方法是抛出异常)。

您可以使用指针(或智能指针)来完成此操作。如果不使用智能指针,请确保代码在删除对象时释放内存。如果你用的是智能笔,就不用担心了。

class Program
{
public:
   Object * myObject;
   Program():
      myObject(new Object())
   {
   }
   ~Program()
   {
       delete myObject;
   }
   // WARNING: Create copy constructor and = operator to obey rule of three.
}

涉及匿名联合和放置new

的技巧

这与jenkas的回答相似,但更直接

class Program
{
public:
   union{
   Object myObject;
   }; //being a union member in this case prevents the compiler from attempting to call the (undefined) default constructor
   
   Program()
   {
      ...
      //Now call the constructor
      new (&myObject) Object(...);
   }
   ~Program()
   {
      myobject.~Object(); //also make sure you explicitly call the object's destructor
   }
}

然而,问题是现在必须显式定义所有特殊的成员函数,因为编译器默认会删除它们。

您可以使用union

联合的成员未初始化

尝试创建union

template <typename T>
union uninitialized_value_of
{ T value; };

当你想要获得一个未初始化的值时。创建一个类型为uninitialized_value_of的对象,并引用其成员value

class Program
{
    uninitialized_value_of<Object> MyObject;
    //Rest of your class
    //...
}
<代码>