Calling a constructor to re-initialize object
Asked Answered
R

13

94

is it possible to re-initialize an object of a class using its constructor?

Radarscope answered 29/1, 2010 at 23:53 Comment(4)
What exactly do you mean by re-initialize?Reddy
Giving the variables same values when they were first created.Radarscope
Neil's solution is "best", though your design needs fixing.Bergen
Is it really necessary to re-initialize the object? Couldn't you just create a new object of the same type with the new values and delete the old one?Geer
S
94

Sort of. Given a class A:

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

the last statement is not initialisation, it is assignment, but it probably does what you want.

Selfassertion answered 29/1, 2010 at 23:56 Comment(8)
+1 Of course this also requires writing a complete, correct assignment operator.Sputter
@Greg This requires HAVING a correct assignment operator, not necessarily WRITING one - the default will often be perfectly OK.Selfassertion
Will this cause a resource leak if a was instantiated using a new keyword?Roband
@Roband in this case you can't instantiate using the new keyword, then you would create A* instead of A. If you are assigning to A* though and you use new you should know where you are deleting this to avoid resource leaks. Instead of writing the delete yourself you are probablt safer using smart pointers.Jolenejolenta
Weirdly, when I do this I get a use of deleted function error.Frothy
Move assignment will be used if supported which reduces at least all the copying that might otherwise occur.Tautology
@JacobGarby is there a reason both the copy assignment and move assignment operators are deleted?Tautology
Is there a way to do this without knowing the type of a? To give an example, if class B : public A, then I would want to have A a = B(); and then later reconstruct variable a with B, but without knowing that it's B specifically.Rectilinear
B
71

Literally? Yes, by using placement new. But first you have to destruct the previously constructed object.

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

The value of this does not go beyond purely theoretical though. Don't do it in practice. The whole thing is ugly beyond description.

Bran answered 30/1, 2010 at 0:5 Comment(12)
Can you explain why you believe this is "ugly beyond description"? It seems to me it has a legitimate use in avoiding code duplication and bugs arising from having a clear method separate from a constructor and destructor.Suchta
What makes this "ugly" is that after calling the destructor on object, you have an uninitialized stack variable. Trying to do anything with object after this point is unsafe. There's not even a way to detect that the object has been destructed later. But once reconstructed, it's valid again. I would say as long as those two lines are back-to-back, this is safe.Onestep
Honestly, I'd probably put this in some kind of vardic template function or even just a macro, to make it clear what's happening and to prevent code being accidentally inserted between the two statements.Soapwort
Incredibly useful to me, since I'm working on implementing a memory pool... Thanks! I agree, though... unless you're taking responsibility for how allocation and deallocation are handled, this is Really Bad Idea. :)Haworth
Well, this is the exact answer for the original question.Gump
Ugly or not, this is a very good solution for re-initialization in static memory applications.Gatekeeper
Can you please explain your comment Final destruction will be done implicitly ?Sufi
@ElProfesor In my example the object is declared locally (or statically), which means that as usual the compiler will perform its destruction at the end of the object's lifetime. I.e. the compiler will automatically/implicitly destruct it.Bran
@AnT Thanks for your explanation, I understand now. If the 2nd call to SomeClass's constructor throws an exception, then the destructor is still called twice when it should be called only once, isn't it?Sufi
Is it necessary to call the destructor first?Lophophore
@Gill Bates: In general case: yes. In some specific cases it is optional.Bran
You can use this feature to initialize Cuda objects in shared memory. Those don't allow constructors ordinarily.Bossuet
D
28

It's possible, although it's a very bad idea. The reason why is that without calling the destructors on the existing object, you are going to leak resources.

With that major caveat, if you insist on doing it, you can use placement new.

// Construct the class
CLASS cl(args);

// And reconstruct it...
new (&cl) CLASS(args);
Dowse answered 30/1, 2010 at 0:2 Comment(12)
You should get a -1 just for suggesting it to someone :P, but a +1 for the hack -- didn't know it, so 0 in totalTien
One could cl->~CLASS(); prior to placement new, as I said on Jared's answer. I'm not sure if that's defined, seems legal.Bergen
+1, only right solution. Nothing evil, it's the way how it's done.Aurangzeb
@Alexander: As the answer stands, it's evil and undefined.Bergen
No, the solution is not correct. It is critical to remember to destruct the "old" object first.Bran
Agreed, destructor needs to be called before placement new.Aurangzeb
@Andrey: It is then legal to completely destruct an object than re-use it's memory?Bergen
if the destructor does nothing you rely on, C++ explicitly allows you to omit calling it before creating a new object. but it doesn't hurt to call it. In the "worst" case, it's implicitly declared and the compiler will optimize out the call. In the best case, the destructor will free memory that's still occupied. But in fact, assignment is the way to go, i think.Commemorate
@GMan: Well, yes. However, when the memory is static or automatic and the object has non-trivial destructor, then the type of the new object must be the same as the type of the old one. Otherwise, the behavior is undefined. Even just adding const leads to undefined behavior. It is all covered in 3.8.Bran
@litb: I knew you secretly watch over everything and chime in randomly. :P And you mean if it's a trivial destructor? @AndreyT: Ah, thanks.Bergen
@GMan, the destructor can contain arbitrary code, it needs not be trivial. For example this is valid code: struct A { A() { cout << "begin"; } ~A() { cout << "end"; } }; int main() { A a; new ((void*)&a) A; } as per 3.8/4: "For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, [...] any program that depends on the side effects produced by the destructor has undefined behavior."Commemorate
@litb: Thanks, I see. I just find it weird you could potentially skip the destruction of an object safely :oBergen
S
17

In C++11, you can do this:

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

This allows you to use Reconstruct passing arbitrary constructor parameters to any object. This can avoid having to maintain a bunch of Clear methods, and bugs that can easily go unnoticed if at some point the object changes, and the Clear method no longer matches the constructor.

The above will work fine in most contexts, but fail horribly if the reference is to a base within a derived object that has a virtual destructor. For this reason, the above implementation prevents use with objects that have a virtual destructor.

Suchta answered 26/5, 2015 at 13:30 Comment(0)
K
13

Short answer:

No. If part of your object's intended behavior is to be initialized several times, then the best way to implement this is through an accessible initialization method. The constructor of your class can simply defer to this method.

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

Nitpicker corner:

Is there some incredibly evil way to call a constructor in C++ after an object is created? Almost certainly, this is C++ after all. But it's fundamentally evil and it's behavior is almost certainly not defined by the standard and should be avoided.

Kwang answered 29/1, 2010 at 23:57 Comment(2)
You could do: T x; x->~T(); new (&x) T(); I'm not sure how defined that is, it almost seems ok. (Ok as in legal, not ok as in this is totally awesome code.)Bergen
Have you read the standard? Is placement new really not defined by it, or are you just assuming that because it seems foreign to you...?Lefkowitz
M
10

No, constructors are only called when the object is first created. Write a new method to do it instead.

Edit

I will not acknowledge placement new, because I don't want to have to get a pet raptor for work.

See this comic, but think of the topic on hand...

Messere answered 29/1, 2010 at 23:55 Comment(0)
L
10

Yes you can cheat and use placement new.
Note: I do not advice this:

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

Personally I would go with constructing a new one. Then move the new object "onto" the old one:

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

Or you could add a method that re-initializes the object. But that goes against your requirement of using the constructor.

Loveinamist answered 30/1, 2010 at 1:36 Comment(2)
In this case new can't fail (in terms of memory allocation) as no memory is allocated. The constructor can fail and throw exceptions just like you would expect.Loveinamist
Possibly what Jagannath means is, what if an exception is thrown, and the object passed as parameter is e.g. an automatic variable belonging to the caller, or a dynamically allocated object held by a smart pointer. When is it valid to call the destructor twice, how does one go about writing code to ensure validity, etc?Coachwhip
C
6

I usually write the following in modern C++ :

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

It may be not the most effective way, as it effectively constructs another object of the same type of a and assigns it to a, but it works in most cases, you don't have to remember the type of a, and it adapts if the type changes.

Cythiacyto answered 30/11, 2016 at 16:48 Comment(0)
P
3

If you really must do this I strongly encourage creating a reset method for this:

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

The above requires A to be copy and move assignable. That is because the initial unoptimized version will copy from a temp. However copy elision may remove this step.

The behavior of A::reset() is always well defined. It replace the existing data in a valid A instance with a data from a new one. Of course any sub-class of A will still need to define its own version if you wanted its data re-initialized as well. However, failure to do so does not in and of itself invoke undefined or unspecified behavior. It simply means that only whatever memory A uses for its members will be reset. Even the involvement of virtual functions and/or virtual inheritance doesn't change this. Although the usual caveats of using such things apply.

Raw pointers will not be deleted by the above so they will need to be put into a std::shared_ptr or similar construct so the will self destruct when no longer needed.

Patronymic answered 2/1, 2021 at 0:26 Comment(0)
L
2

May-be not what you have in mind, but since you didn't mention what it is for, I suppose one answer would be that you'd do it by controlling scope and program flow.

For example, you wouldn't write a game like this:

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

Instead you'd strive for:

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

(Very rough outline to convey the idea, not meant to be remotely compilable.)

Lantana answered 30/1, 2010 at 0:25 Comment(0)
T
2

Instead of destructing and reinitializing as suggested by some of the answers above, it's better to do an assignment like below. The code below is exception safe.

    T& reinitialize(int x, int y)
    {
        T other(x, y);
        Swap(other); // this can't throw.
        return *this;
    }
Twerp answered 30/1, 2010 at 4:51 Comment(1)
What is other???Lefkowitz
S
0

While most answers are reinitializing an object in two steps; first, creating an initial object, and second creating another object and swapping it with the first one using placement new, this answer covers the case that you first create a pointer to an empty object and later allocate and construct it:

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 
Shuster answered 28/6, 2019 at 16:4 Comment(0)
S
0

Yes , it is possible. If you create a method that returns a new object.

#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;
}
Stupid answered 7/11, 2020 at 19:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.