调用构造函数重新初始化对象

问题描述 投票:0回答:13

是否可以使用类的构造函数重新初始化类的对象?

c++ constructor
13个回答
93
投票

有点。给定 A 级:

A a;
...
a = A();   

最后一条语句不是初始化,而是赋值,但它可能会执行您想要的操作。


67
投票

字面意思?是的,通过使用新的展示位置。但首先你必须破坏之前构造的对象。

SomeClass object(1, 2, 3);
...
object.~SomeClass(); // destruct
new(&object) SomeClass(4, 5, 6); // reconstruct
...
// Final destruction will be done implicitly

这一点的价值并没有超出纯粹的理论范围。实际操作中不要这样做。整件事丑陋得无法形容。


28
投票

这是可能的,尽管这是一个非常糟糕的主意。原因是,如果不调用现有对象的析构函数,就会泄漏资源。

有了这个主要警告,如果你坚持这样做,你可以使用新的放置。

// Construct the class
CLASS cl(args);

// And reconstruct it...
new (&cl) CLASS(args);

17
投票

在C++11中,你可以这样做:

#include <type_traits>

template <class T, typename... Args>
void Reconstruct(T& x, Args&&... args)
{
    static_assert(!std::has_virtual_destructor<T>::value, "Unsafe"); 
    x.~T();
    new (&x) T(std::forward<Args>(args)...);
}

这允许您使用

Reconstruct
将任意构造函数参数传递给任何对象。这可以避免维护一堆
Clear
方法,以及如果在某个时刻对象发生变化并且
Clear
方法不再与构造函数匹配时很容易被忽视的错误。

上面的方法在大多数情况下都可以正常工作,但如果引用的是具有虚拟析构函数的派生对象中的基类,则会严重失败。因此,上述实现阻止使用具有虚拟析构函数的对象。


13
投票

简短回答:

不。如果对象的部分预期行为需要多次初始化,那么实现此目的的最佳方法是通过可访问的初始化方法。类的构造函数可以简单地遵循此方法。

class C1 {
public:
  C1(int p1, int p2) {
    Init(p1,p2);
  }
  void Init(int p1, int p2) { ... }
};

挑剔角:

在创建对象后,是否有一些极其邪恶的方法可以在 C++ 中调用构造函数?几乎可以肯定,这毕竟是 C++。但它从根本上来说是邪恶的,而且它的行为几乎肯定不是由标准定义的,应该避免。


10
投票

不,构造函数仅在对象首次创建时调用。编写一个新方法来代替。

编辑

我不会承认新的安置,因为我不想为了工作而必须得到一只宠物猛禽。

看到这个漫画,但想到手头上的话题...


9
投票

是的,您可以作弊并使用新的展示位置。
注意:我不建议这样做:

#include <new>

reInitAnA(A& value)
{
    value.~A();            // destroy the old one first.
    new (&value) A();      // Call the constructor 
                           // uses placement new to construct the new object
                           // in the old values location.
}

就我个人而言,我会选择建造一个新的。然后将新对象移动到旧对象上:

#include <new>

reInitAnA(A& value)
{
    value = A{};     // Construct a temporary.
                     // Use assignment to move (or copy when no
                     // move is available).
                     //
                     // destructor of temp then called.
}

或者您可以添加一个方法来

re-initializes
对象。但这违背了您使用构造函数的要求。


5
投票

我通常用现代 C++ 编写以下内容:

SomeClass a;
...
a = decltype(a)();

这可能不是最有效的方法,因为它有效地构造了另一个相同类型

a
的对象并将其分配给
a
,但它在大多数情况下都有效,你不必记住
的类型a
,如果类型发生变化,它也会适应。


2
投票

可能不是您想要的,但由于您没有提到它的用途,我想一个答案是您可以通过控制范围和程序流程来做到这一点。

例如,你不会编写这样的游戏:

initialize player
code for level 1
...
reinitialize player
code for level 2
...
etc

相反,你会努力:

void play_level(level_number, level_data) {
    Player player; //gets "re-initialized" at the beginning of each level using constructor
    //code for level
}

void game() {
    level_number = 1;
    while (some_condition) {
        play_level(level_number, level_data);
        ++level_number;
    }
 }

(非常粗略的轮廓来表达这个想法,并不意味着可以远程编译。)


2
投票

与其按照上面一些答案的建议进行破坏和重新初始化,不如进行如下所示的分配。下面的代码是异常安全的。

    T& reinitialize(int x, int y)
    {
        T other(x, y);
        Swap(other); // this can't throw.
        return *this;
    }

2
投票

如果你真的必须这样做,我强烈建议为此创建一个重置方法:

class A
{
 ...
  public:
    reset() { *this= A() };
}

上面要求A是可复制可移动可赋值的。 这是因为初始未优化的版本将从临时版本复制。然而,复制省略可能会删除此步骤。

A::reset() 的行为始终是明确定义的。它将有效 A 实例中的现有数据替换为新实例中的数据。当然,如果您希望 A 的数据也重新初始化,A 的任何子类仍然需要定义自己的版本。然而,不这样做本身并不会引发未定义或未指定的行为。它只是意味着只有 A 用于其成员的内存才会被重置。即使虚拟函数和/或虚拟继承的参与也不会改变这一点。尽管使用此类东西的通常警告适用。

原始指针不会被上述删除,因此需要将它们放入 std::shared_ptr 或类似的构造中,以便不再需要时会自毁。


0
投票

虽然大多数答案都是分两步重新初始化对象;首先,创建一个初始对象,然后创建另一个对象并使用

placement new
与第一个对象交换,这个答案涵盖了您首先创建一个指向空对象的指针,然后分配和构造它的情况:

class c *c_instance; // Pointer to class c
c_instance = new c(arg1, ..., argn) // Allocate memory & call the proper constructor 
// Use the instance e.g. c->data
delete c_instance; // Deallocate memory & call the destructor 

0
投票

是的,这是可能的。 如果您创建一个返回新对象的方法。

#include "iostream"
class a // initialize class
a getNewA(a object){// Create function to return new a object
a new_object(/*Enter parameters for constructor method*/);
return new_object;
}
© www.soinside.com 2019 - 2024. All rights reserved.