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

时间:2010-01-29 23:53:32

标签: c++ constructor

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

13 个答案:

答案 0 :(得分:70)

排序。鉴于A类:

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

最后一个语句不是初始化,而是赋值,但它可能会做你想要的。

答案 1 :(得分:44)

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

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

这个价值并没有超出纯粹的理论范围。不要在实践中这样做。整个事情都难以描述。

答案 2 :(得分:28)

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

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

// Construct the class
CLASS cl(args);

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

答案 3 :(得分:11)

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

修改

我不会承认新的位置,因为我不想让宠物猛禽去上班。

See this comic,但想想手头的话题......

答案 4 :(得分:10)

简短回答:

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

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

Nitpicker角落:

在创建对象后,是否有一些令人难以置信的邪恶方式在C ++中调用构造函数?几乎可以肯定,这毕竟是C ++。但它基本上是邪恶的,它的行为几乎肯定不是由标准定义的,应该避免。

答案 5 :(得分:9)

在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方法不再与构造函数匹配,则很容易忽略这些错误。

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

答案 6 :(得分:7)

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

#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.
}

答案 7 :(得分:4)

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

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

这可能不是最有效的方式,因为它有效地构建了同一类型a的另一个对象并将其分配给a,但它在大多数情况下都有效,你不会这样做。必须记住a的类型,如果类型发生变化,它会适应。

答案 8 :(得分: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;
    }
 }

(传达这个想法的粗略轮廓,并不意味着可远程编辑。)

答案 9 :(得分:2)

不像上面的一些答案所暗示的那样破坏和重新初始化,最好像下面那样进行分配。下面的代码是异常安全的。

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

答案 10 :(得分: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 

答案 11 :(得分: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;
}

答案 12 :(得分:0)

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

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

以上要求 A 是可复制和移动的。 那是因为最初的优化版本将从临时文件中复制。但是复制省略可能会删除这一步。

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

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