C++, is it possible to call a constructor directly, without new?
Asked Answered
R

10

62

Can I call constructor explicitly, without using new, if I already have a memory for object?

class Object1{
    char *str;
public:
    Object1(char*str1){
        str=strdup(str1);
        puts("ctor");
        puts(str);
    }
    ~Object1(){
        puts("dtor");
        puts(str);
        free(str);
    }
};

Object1 ooo[2] = {
     Object1("I'm the first object"), Object1("I'm the 2nd")
};

do_smth_useful(ooo);
ooo[0].~Object1(); // call destructor
ooo[0].Object1("I'm the 3rd object in place of first"); // ???? - reuse memory
Rachaba answered 22/3, 2010 at 17:44 Comment(13)
Oh yeah, this seems interesting. I'd answer that it is not possible but I'd better wait before making a fool of myself. I'm def faving this.Billhook
dup: #314382 and #1125134Lithography
short answer is No, long answer is there is a way by which memory reuse can be achieved, its called placment new. But keep in that placement new has uses other than reusing memory.Appertain
Is there a reason you don't just create an assignment operator?Boyhood
@Dennis Zickefoose, yes. deleting and reconstructing is in different methodsRachaba
@osgx: even so, before using placement new you should fully understand the "rule of three" in C++, and appreciate all the errors you can make when you introduce an unusual object lifecycle. Your class as written is a likely source of double-free errors. If you want to free the memory early, then instead of calling the destructor you could define a function "release()". Call that after do_smth_useful, perhaps also call it from the destructor, and then use operator= or a "set" function for the new value.Passerby
So call those methods from the asignment operator. That is the correct way to deal with assignment.Boyhood
@Dennis: copy-and-swap is the correct way to deal with assignment :-). So yes, call those functions from the assignment operator, but not in the most obvious way.Passerby
no-no-no. I want to make an STL-like container (for learning and with only very basic functionality). This container will store Objects (any object, just like STL) in array, allow me to add and to remove them.Rachaba
@osgx: OK, so Object1 is actually going to a template parameter, and it's not your container's fault that it's broken. In that case I withdraw my objection to using placement new, Your Honour, but it should still be noted for the record that Object1 o; mycontainer.push_back(o); will result in undefined behaviour sooner or later.Passerby
@Steve Jessop, i will need to destruct object only in void pop(), when deleting object from container. As I want to use array of objects (not pointer) to be stored in container, I will need to destruct one, but not use delete for it.Rachaba
@Steve Jessop, #2097071 answer from Jan 19 at 20:52 UncleBens. He says, that stl vector does use THE SANE TECHNIQUE with placement new and direct calls to ~T()Rachaba
Yes, the problem is in the class Object1, not in the placement new technique. Its copy constructor and assignment operator result in two objects sharing the same malloc ed buffer. You should either implement them, or else declare them private member functions (and not define them).Passerby
P
87

Sort of. You can use placement new to run the constructor using already-allocated memory:

 #include <new>

 Object1 ooo[2] = {Object1("I'm the first object"), Object1("I'm the 2nd")};
 do_smth_useful(ooo);
 ooo[0].~Object1(); // call destructor

 new (&ooo[0]) Object1("I'm the 3rd object in place of first");

So, you're still using the new keyword, but no memory allocation takes place.

Patience answered 22/3, 2010 at 17:45 Comment(7)
Yes, the direct destructor call is in fact necessary to allow the object to release any resources, before overwriting the object with a newly constructed object.Relaxation
Yeah, I updated the sample. The Object1 constructor calls strdup and destructor must to free(str).Rachaba
destructor call is allowed. C++98: [class.dtor] paragraph 13: X*p; ... p->X::~X();Rachaba
+1 - though strictly speaking, "placement new" isn't exactly "without new" ;-)Hydrangea
@Steve314: I know, that's why I pointed out that the keyword is still there, but no allocation is taking place.Patience
There should not be a big "Yes" on the top, It is misleadingAppertain
Also, beware that disaster will strike if the constructor throws. The object will be left uninitialised, but the destructor will still be called at some point in the future.Blooming
M
20

Let me show you some code on how it can be done, both in construction and destruction

#include <new>

// Let's create some memory where we will construct the object.
MyObject* obj = (MyObject*)malloc(sizeof(MyObject));

// Let's construct the object using the placement new
new(obj) MyObject();

// Let's destruct it now
obj->~MyObject();

// Let's release the memory we used before
free(obj);
obj = 0;

I hope the above summary makes things clearer.

Morrison answered 22/3, 2010 at 18:6 Comment(0)
H
17

I think you're looking for Placement New. The C++ FAQ Lite has a good summary of how you do this. There are a few important gotchas from this entry:

  1. You're supposed to #include <new> to use the placement new syntax.
  2. Your memory buffer needs to be properly aligned for the object you are creating.
  3. It's your job to manually call the destructor.
Heyduck answered 22/3, 2010 at 17:47 Comment(4)
You have to #include a library just to use some C++ syntax? I'm not contradicting you - I just think this is really wierd.Hydrangea
@Steve314: The C++ syntax is giving arguments to new, which are passed through to a matching overload of operator new. You need the library to provide the required overload, operator new(size_t,void*).Blooming
I'm not aware of a need to call operator new - I already have the memory allocated, and I thought the placement new was just calling the constructor. I've certainly been getting away without worrying about this, though (1) it's possible I have included <new> somewhere, and (2) it's always possible that my compiler is letting me get away with something naughty. Time to review things, I guess, and see if I'm doing something wrong.Hydrangea
It calls a replacement operator new which is defined for you in the standard library which doesn't allocate any memory, it just returns the memory you passed into it. Then the constructor is called as usual, thus achieving what you wanted. it's not really syntax, it's a redefined operator new that basically just returns it's extra paramaterPoleyn
E
6

Literally speaking, NO, you can't do it without the "new" keyword. See all the answers about placement new for the way to use the "new" keyword to call the constructor without actually allocating memory.

Ending answered 22/3, 2010 at 17:57 Comment(16)
Anyone know the relevant section of the spec that defines this?Menton
@nmr: Did you mean the section that shows that placement new does do it, or the section(s) that show that the constructor can't be re-called in other ways explicitly in any other way.Ending
the latter -- that constructors can't be called directly.Menton
@JaveneCPPMcGowan: Libraries use placement new. Only the compiler itself can call the constructor without placement new.Ending
@Ben Voigt I made the comment because I got the understanding that I needed to include new to use placement new. I want to verify, you dont need to include new to use that syntax. However, it is sad we must use a syntax I never heard about, instead of obj.std::string() for example. I dont see why ctors dont have a name, and why c++ have to be so complicated and full of syntax.Aurar
@JaveneCPPMcGowan: If constructors had a name, you could take their address and later call them through a function pointer. On any line obj.*pmf() you couldn't tell if it were a constructor call (killing the old object by reusing it) or not. By making constructor invocation a unique syntax it separates it from ordinary function call. There are many many rules that give constructors different semantic behavior from other function calls, so different syntax strengthens the semantics instead of confusing them.Ending
@Ben Voigt I think you have to do better than that. trying to call a member/name of an object via (.)operator/ (->) operator/ (.*) operator/ (->*) operator is a static compile time operation. Meaning you know exactly the type of the object before compilation, and you cannot use names from an object that is not a member of the object. In short, it is clear that you are calling a constructor.Aurar
@JaveneCPPMcGowan: You would know it is a member; you would not know it is a constructor.Ending
@JaveneCPPMcGowan: Because once you create a function pointer, you no longer know the name of what it points to...Ending
@Ben Voigt I am sure you meant object pointer. As I said, the information is right there. std::string *s; s->vector() is clearly wrong. If you talking about dynamic type, I only care about static type here. Programmer would be responsible to handle runtime type and cast appropriately.Aurar
@JaveneCPPMcGowan: You are wrong, I meant function pointer. That is why you are not understanding my example of obj.*pmf() which uses a pointer-to-member-function. You are thinking about indirection to the object, and I am thinking about indirection to the function. If a constructor could be invoked indirectly interchangeably with any other member function, reasoning about object lifetime in the presence of member function pointers would be a lot harder than it is now.Ending
I dont think ctor is really concerned with lifetime of object. It is a function designed to set data members in a valid state and create any necessary resources. For example, new calls ALLOCATES memory but also calls the ctor for us. But if we dont call delete, the lifetime of the object is indefinite. point: ctor has nothing to do with the lifetime of an object.Aurar
@JaveneCPPMcGowan: Here, object lifetime rules. Notice the many mentions of "construction": eel.is/c++draft/basic.lifeEnding
Quoted from your link when lifetime of object ends: if T is a non-class type, the object is destroyed, or (1.4) if T is a class type, the destructor call starts, or (1.5) the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).Aurar
@JaveneCPPMcGowan: Yes, constructing a new object hits that "reused by an object that is not nested within...." case. Calling a constructor (other than a delegating constructor, in which case only one of the constructor calls counts) constructs a new object. The lifetime of the one that previously occupied the same storage immediately ends.Ending
@Ben Voigt I was unable to compile your paragraph. Syntax not parsable.Aurar
N
2

Yes, when you've got your own allocated buffer you use placement new. Brian Bondy has a good response here in a related question:

What uses are there for "placement new"?

Nonmetallic answered 22/3, 2010 at 17:47 Comment(0)
U
1

You can call a destructor, but memory will not be reclaimed, and your call will be equivalent to a function call. You have to remember that underneath the destructor does 2 things: destructs object based on your specification, and reclaims the memory. Since you dtor will be called anyway for an object allocated on the stack, calling it twice may result in an undefined behavior.

Undemonstrative answered 22/3, 2010 at 17:47 Comment(3)
Au contraire, if Object1 holds pointers to things that need to be deleted, the explicit destructor call will ensure that happens, before overwriting the object with a newly constructed object. Then the automatic stack destructor call will destruct the newly constructed object, so you're not calling it twice on the same object.Relaxation
but in my sample code, I create 2 Object1 in initializer, then destruct the first and recreate (reconstruct) in place of the 1st the 3rd object. When this block is closed, ooo[2] will call two destructors. So this sample is normal? Does destructor reclaims memory by itself, or only when used with delete or implicit 'delete' when stack is shrinked?Rachaba
The destructor doesn't reclaim the memory of the object being destroyed, but it sure can call delete (or delete[], or free, or HeapFree, etc) on additional memory that object had owned. It's that related memory that would be reclaimed when the destructor runs.Ending
H
1

Yes, using placement new - as above, but you might consider having a second factory class to manage the storage, even if it means copying an object. memcpy() is generally cheap for small objects.

Ha answered 22/3, 2010 at 17:53 Comment(1)
Can I really do a memcpy for object? I want to write rather universal container, like STL vector. Some objects can depend on its address (store inside itself address)Rachaba
K
0

You can use the following template

template <typename T, typename... Args>
inline void InitClass(T &t, Args... args)
{
    t.~T();
    new (&t) T(args...);
}

usage:

struct A
{
   A() {}
   A(int i) : a(i) {}
   int a;
} my_value;

InitClass(my_value);
InitClass(my_value, 5);
Kaela answered 26/3, 2019 at 2:37 Comment(0)
L
0

The problem is that normally you need an already existing object on which to call the functions (unless it is a static function), so for the constructor that's a bit of a an issue when you want to do it in a nonstandard way.

Here's how to create a new class object in existing memory without using new. This will even work with raw memory from malloc(), provided you first memset() the memory to zeros.

#include <string>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>

class Str_obj{
public:
  char* str;
  constexpr Str_obj(const Str_obj& obj) = default;
  Str_obj(const char* str1){
    str = strdup(str1);
    puts("ctor");
    puts(str);
  }
  ~Str_obj(){
    puts("dtor");
    puts(str);
    free(str);
    str = nullptr;
  }
  Str_obj& operator = (const Str_obj& obj){
    puts("copy assign");
    free(str);
    str = strdup(obj.str);
    return *this;
  }
  Str_obj& operator = (Str_obj&& obj){
    puts("move assign");
    free(str);
    str = obj.str;
    obj.str = nullptr;
    return *this;
  }
};

std::vector<std::string>
to_string_vec(Str_obj* arr, size_t len){
  std::vector<std::string> str_arr(len);
  for(int i = 0; i < len; ++i){
    str_arr[i] = std::string(arr[i].str);
  }
  return str_arr;
}

int main() {
  std::cout<<"Creating objects:\n";
  constexpr size_t len = 2;
  Str_obj arr[len] = {
    Str_obj("I'm the first object"),
    Str_obj("I'm the 2nd")
  };
  std::vector<std::string> str_arr1 = to_string_vec(arr, len);

  std::cout<<"\n";
  std::cout<<"Destruct and replace object:\n";
  arr[0].~Str_obj(); // call destructor

  // Calls constructor, then move assign to existing memory.
  arr[0] = std::move(Str_obj("I'm the 3rd object in place of first"));
  std::vector<std::string> str_arr2 = to_string_vec(arr, len);

  std::cout<<"\n";
  for(int i = 0; i < len; ++i){
    std::cout<<i<<". "<<str_arr1[i]<<"\n";
  }
  std::cout<<"\n";
  for(int i = 0; i < len; ++i){
    std::cout<<i<<". "<<str_arr2[i]<<"\n";
  }
  return 0;
}

Possible output:

Creating objects:
ctor
I'm the first object
ctor
I'm the 2nd

Destruct and replace object:
dtor
I'm the first object
ctor
I'm the 3rd object in place of first
move assign
dtor

0. I'm the first object
1. I'm the 2nd

0. I'm the 3rd object in place of first
1. I'm the 2nd
dtor
I'm the 2nd
dtor
I'm the 3rd object in place of first
Leastways answered 17/12, 2023 at 21:47 Comment(0)
H
-3

Based on comments, this only works for Microsoft C++ compilers

Quite simply, without new:

    imguistate = (int *)malloc(ImGui::GetInternalStateSize());
    memset(imguistate, 0, ImGui::GetInternalStateSize());
    ((ImGuiState *)imguistate)->ImGuiState::ImGuiState();

This works with any class:

class SomeClass {
public:
    SomeClass() {
        printf("Called constructor\n");
    }
};

int main () {
    SomeClass *someclass = new SomeClass;
    someclass->SomeClass::SomeClass(); // call constructor again
}
Hyperemia answered 5/8, 2016 at 0:12 Comment(5)
What is ImGuiState::ImGuiState()? Will this work for any class?Rachaba
Yes, it works with any class, extended answer with custom class.Hyperemia
No, it doesn't work in C++ (do read first word of question title, parse it with all punctuation). gcc (g++) says "error: cannot call constructor 'SomeClass::SomeClass' directly". clang++ says "error: cannot refer to type member 'SomeClass' in 'SomeClass' with '->'". What is wrong with your compiler, is it Microsoft's ManagedC++? Does it have any standard or something which may resemble anything like standdard compliance?Rachaba
@Rachaba I'm using Microsoft Visual Studio 2015 Community Edition (unmanaged C++). Thanks for the info, did not know that it won't work in gcc/clang.Hyperemia
Seems undocumented: msdn.microsoft.com/en-us/library/hh567368.aspx / msdn.microsoft.com/en-us/library/34h23df8.aspx. Try /Za, /Ze, /Zc optionsRachaba

© 2022 - 2024 — McMap. All rights reserved.