osgx
osgx

Reputation: 94225

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

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

Upvotes: 63

Views: 38804

Answers (10)

Qwert Yuiop
Qwert Yuiop

Reputation: 152

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

Upvotes: 0

user11258054
user11258054

Reputation: 1

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

Upvotes: 0

kungfooman
kungfooman

Reputation: 4893

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
}

Upvotes: -3

unwind
unwind

Reputation: 399803

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.

Upvotes: 88

Cthutu
Cthutu

Reputation: 8907

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.

Upvotes: 20

Ben Voigt
Ben Voigt

Reputation: 283634

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.

Upvotes: 6

Martin Beckett
Martin Beckett

Reputation: 96109

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.

Upvotes: 1

Michael Kristofik
Michael Kristofik

Reputation: 35188

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.

Upvotes: 17

itsmatt
itsmatt

Reputation: 31406

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"?

Upvotes: 2

vehomzzz
vehomzzz

Reputation: 44578

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.

Upvotes: 1

Related Questions