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

Posted

技术标签:

【中文标题】在 C++ 中定义对象而不调用其构造函数【英文标题】:Defining an object without calling its constructor in C++ 【发布时间】:2011-11-25 07:24:48 【问题描述】:

在 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(...);
   


【问题讨论】:

为什么不使用动态初始化? auto_ptr/shared_ptr? 全局范围或类成员?您的代码与您的问题不符。 调用默认构造函数然后在初始化之后将其设置为您关心的对象有什么问题,就像您的代码一样?或者只是让它成为一个指针:Object* myObj; 【参考方案1】:

存储指向 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 而不是原始内存指针。

【讨论】:

如果你创建了一个析构函数,你应该遵守三法则。 @RomanKruglov • 一个std::unique_ptr 就可以了,只要你还= delete 复制构造函数和赋值运算符,或者实现它们来做正确的事情。 你的意思是delete myObjectObjectProgram 的析构函数中? @Eljay:为什么需要= delete 复制构造函数/赋值运算符才能使用std::unique_ptr?我的理解是,如果类的任何成员有一个已删除的复制构造函数,the class's copy constructor/assignment operators are implicitly deleted(如果即使单个 unique_ptr 是实例状态的一部分,您也必须显式编写它们)。 @Eljay:这就是我之前链接的要点 program2 = program; 将失败,因为只需拥有一个 std::unique_ptr 成员(删除它自己的复制构造函数/复制赋值),它自己的复制构造函数/assignment 也被删除。显式使用= delete; 是多余的;编译器已经删除了它们(正是为了防止您描述的问题)。你描述的问题听起来像an issue with the old std::auto_ptr,它在副本上转移了所有权; std::unique_ptr 阻止了这种行为。【参考方案2】:

其他人发布了使用原始指针的解决方案,但智能指针会更好:

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 &amp;)

如果你想避免单独的堆分配,这可以通过 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();
  
;

【讨论】:

使用你的第一个建议,我得到The text "&gt;" is unexpected. It may be that this token was intended as a template argument list terminator but the name is not known to be a template.【参考方案3】:

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

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(....);
   


此解决方案的一大优势在于,它不需要任何额外的分配,并且对象内存分配正常,但您可以在调用构造函数时进行控制。

Another sample link

【讨论】:

【参考方案4】:

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

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;
;

【讨论】:

【参考方案5】:

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

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*/)
      
      ...
      
  ;

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

【讨论】:

我刚遇到这个问题。如果我在初始化列表中调用指定的构造函数,它就像一个魅力。如果我不这样做,将调用默认构造函数。我不明白编译器是如何知道稍后我初始化对象的,因为此时程序遇到我的对象MyClass obj; 的声明,编译器没有迹象表明“我不会在这里调用默认构造函数,我应该等到初始化列表” @CătălinaSîrbu - 我不完全确定,但我猜也许类声明的顺序让你认为这是初始化的顺序。初始化列表实际上是在构造类的其他任何事情之前完成的,因此在构造函数中设置您稍后需要的东西是很有用的。【参考方案6】:

您可以使用指针(或智能指针)来执行此操作。如果您不使用智能指针,请确保在删除对象时您的代码释放内存。如果您使用智能指针,请不要担心。

class Program

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

【讨论】:

【参考方案7】:

一个涉及匿名联合和放置新的技巧

这类似于 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
   


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

【讨论】:

以上是关于在 C++ 中定义对象而不调用其构造函数的主要内容,如果未能解决你的问题,请参考以下文章

11.8 C++构造函数小节

11.8 C++构造函数小节

11.8 C++构造函数小节

在 php 中调用类方法(带构造函数)而不实例化对象

c++,类的对象作为形参时一定会调用复制构造函数吗?

在c++中如何调用数组对象的构造函数