注意事项和调用构造函数和析构函数的风险是常见的方法吗?

时间:2015-09-15 15:38:34

标签: c++ constructor destructor

我的程序中有一点需要重置某个对象的状态"出厂默认值"。任务归结为执行在析构函数和构造函数中编写的所有内容。我可以删除并重新创建对象 - 但是我可以将析构函数和构造函数作为普通对象调用吗? (特别是,我不想将更新的指针重新分发到新实例,因为它在程序的其他位置存在副本)。

  MyClass {
  public:
        MyClass();
        ~MyClass();

  ...      
  }


  void reinit(MyClass* instance)
  {
        instance->~MyClass();
        instance->MyClass();
  }

我可以这样做吗?如果是这样,是否有任何风险,警告,我需要记住的事情?

6 个答案:

答案 0 :(得分:3)

如果正确编写了赋值运算符和构造函数,则应该能够将其实现为:

void reinit(MyClass* instance)
{
    *instance = MyClass();
}

如果您的赋值运算符和构造函数编写不正确,请修复它们。

实现重新初始化作为破坏后跟构造的警告是,如果构造函数失败并抛出异常,对象将被破坏两次而不会在第一次和第二次破坏之间再次构建(一次是通过手动破坏,并且一旦它的所有者超出范围就会发生自动销毁)。这有不明确的行为。

答案 1 :(得分:2)

您可以使用placement-new

void reinit(MyClass* instance)
{
    instance->~MyClass();
    new(instance) MyClass();
}

所有指针仍然有效。

或作为会员功能:

void MyClass::reinit()
{
    ~MyClass();
    new(this) MyClass();
}

这应该谨慎使用,请参阅http://www.gotw.ca/gotw/023.htm,这是关于使用此技巧实现一个分配运算符,但这里也适用一些要点:

  • 构造函数不应抛出
  • MyClass不应用作基类
  • 它会干扰RAII,(但这可能是想要的)

Credit to Fred Larson

答案 2 :(得分:1)

  

我可以这样做吗?如果是这样,是否有任何风险,警告,我需要记住的事情?

不,你不能这样做。除了它在技术上可能用于析构函数调用之外,它只是未定义的行为。

假设您已经实现了类correctly的赋值运算符,您可以写:

void reinit(MyClass* instance) {
    *instance = MyClass();
}

答案 3 :(得分:1)

你应该使用智能指针并依靠移动语义来获得你想要的行为。

auto classObj = std::make_unique<MyClass>();

这会创建一个包装指针,为您处理动态内存。假设您已准备好将classObj重置为出厂默认值,您只需:

classObj = std::make_unique<MyClass>();

这&#34;移动任务&#34;操作将调用MyClass的析构函数,然后重新分配classObj智能指针以指向新构造的MyClass实例。泡沫,冲洗,必要时重复。换句话说,您不需要reinit功能。然后,当classObj被销毁时,其内存将被清除。

答案 4 :(得分:1)

instance->MyClass();是非法的,您必须收到编译错误。

instance->~MyClass();是可能的。这可以做两件事之一:

  • 没有,如果MyClass有一个简单的析构函数
  • 运行析构函数中的代码并结束对象的生命周期,否则。

如果在生命周期结束后使用对象,则会导致未定义的行为。

除非您首先使用 placement new 创建对象,否则编写instance->~MyClass();的情况很少见,或者您即将使用展示位置重新创建对象。

如果您不知道, placement new 会在您已分配存储空间时创建对象。例如,这是合法的:

{
    std::string s("hello");
    s.~basic_string();
    new(&s) std::string("goodbye");

    std::cout << s << '\n';
}

答案 5 :(得分:0)

您可以尝试使用placement new表达式

new (&instance) MyClass()