-1

I got a program where i got a class "agent" and there are a number of sub-classes that implement the functions of agent (and have extra fields (int float etc). The idea is that i can iterate through a vector of agent pointers and call the functions regardless of the underlying class.

So far so good.

I got more experience with c, so i allocated memory with malloc (i know this isn't the right way but hold on for a sec).
Like this:

bee* b = (bee*) std::malloc(sizeof(bee));

Now i thought i got memory allocated and a pointer pointing on the memory telling me (or more precisely c++) where what is and what functions i can call. And in fact i can do all of that. But when i push_back() that pointer in my vector of agents and then try to call the functions my program just crashes without any error messages. (Also debugging isn't helpful or i am to bad at it).

I can fix that problem by either:

bee* b = (bee*) std::malloc(sizeof(bee));
b = new (b) bee();  // calling the constructor manually

or

bee* b = new bee;   // using the c++ way of allocating which automatically calls the constructor

That begs 2 Questions:

  • What does the default constructor do
  • And why does it solve the error.

Thank you for your help.

Edit:

I got told that the constructor "initializes the object". I seem to have no idea what that means. In my understanding i have a piece of memory and "i know what is stored there" and its "overwrite protected" or "allocated". What is the difference between that state and the initialized one. What more information is there? Where is an initialization stored. I don't get it. Don't i know from the pointer that one class is the subclass of another class ?

qub3
  • 75
  • 7
  • 3
    The default constructor (which I think is what you mean by the empty or not defined constructor) calls the default constructor for all subobjects. – john Jun 11 '23 at 10:27
  • 1
    The error is solved because using a uninitialized object is *undefined behaviour*. By calling any constructor, you initialize the object. – john Jun 11 '23 at 10:29
  • Your 'b = new(b) bee();' is called an inplace new. It will construct the object in the memory allocated by malloc by calling its constructor (malloc will never result in an object in a valid state). – Pepijn Kramer Jun 11 '23 at 10:29
  • Note that in C++ you should neither use malloc nor new (unless deep down inside a datatstructure). Either you put objects in containers (like std::vector) which will handle memory allocation, or you use std::make_unique(); That will return you an object that will delete the memory when the unique_ptr goes out of scope (Look up RAII) – Pepijn Kramer Jun 11 '23 at 10:31
  • When programming in C++, whenever you feel the need to do a C-style cast, you should generally take it as a sign that you're doing something wrong. – Some programmer dude Jun 11 '23 at 10:32
  • 1
    Also, instead of using `std::malloc` you can use the `operator new` *function*, as in `reinterpret_cast(operator new(sizeof(bee)))` – Some programmer dude Jun 11 '23 at 10:33
  • 2
    To answer your follow up question. In C++ class or struct objects must be initialized before they can be used. The only way to initialize such an object is to call one of it's constructors. If you use an uninitialized class then your program has undefined behaviour, which means that the C++ standard imposes no requirements on how your program will behave. That's really all there is to it. If you want to use a class object, you must call one of it's constructors first. – john Jun 11 '23 at 11:00
  • 1
    Whether you do or do not know what is stored in some piece of memory is irrelevant. If you start reasoning like that you are inventing your own language which is different from C++. – john Jun 11 '23 at 11:02
  • A _"not defined constructor"_ does actually nothing. You're probably confusing some terminology. Did you mean something like _default compiler generated constructor_ maybe?? – πάντα ῥεῖ Jun 11 '23 at 11:06
  • You should read [Object](https://en.cppreference.com/w/cpp/language/object) as this will help you understand when a Standard C++ Object (not an OOP object) is created. Not correctly creating a C++ Object and then trying to use it usually leads to Undefined Behaviour for which no diagnostic is required. – Richard Critten Jun 11 '23 at 11:43
  • Why are you hacking about like this? Just call `new bee` and be done with it. – Paul Sanders Jun 11 '23 at 15:50
  • Does this answer your question? [Malloc and constructors](https://stackoverflow.com/questions/2995099/malloc-and-constructors) – Jan Schultke Jun 11 '23 at 20:54

1 Answers1

3

malloc() allocates memory but does not create objects in C++. That's why you cannot simply call malloc() and pretend there's an object in that memory. More specifically, malloc() does not call any constructor, and many C++ types cannot be used without a constructor being called.

It's like building a chicken coop and expecting to get eggs: you've missed the most important step.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • ok understood but why doesn't the code below throw an error, but only when there is an agent pointer pointing on my variable. That seems to me, to stay in your example, that i can "build a chicken coop, expect eggs, but when i sell my choop, the chicken are gone for the new owner. Here is the code. ```bee* b = (bee*) std::malloc(sizeof(bee)); b->init(some variables);``` – qub3 Jun 11 '23 at 11:15
  • that is why i even bothered to ask this, if i instantly got an error i wouldn't even cared – qub3 Jun 11 '23 at 11:17
  • 7
    @qub3 _"why doesn't the code below throw an error"_ - the code has undefined behavior. You shouldn't expect anything from it. – Ted Lyngmo Jun 11 '23 at 11:21
  • 1
    @qub3: Some types will appear to work if you just `malloc()` them, even though it is not valid C++. Read about "undefined behavior" - it's a nasty thing which means your code could behave in any way, including a different way every time you run it. – John Zwinck Jun 11 '23 at 13:12
  • @qub3 • *why doesn't the code below throw an error* Why should the code throw an error? It's not programmed to throw an error. The standard doesn't mention throwing an error. It *could* throw an error, but it could also email your browser history to your grandmother and then format your hard drive... undefined behavior is funny that way — any observed behavior is allowed. – Eljay Jun 11 '23 at 19:10
  • 1
    It is legal to "pretend there's an object in memory", as long it's an [implicit-lifetime type](https://en.cppreference.com/w/cpp/named_req/ImplicitLifetimeType). – Jan Schultke Jun 11 '23 at 22:26