I'm still new to c++, apologies if this is obvious, but I could not find a good answer after much googling.
I wish I could write the following code.
class Test {
public:
Test();
private:
std::unique_ptr<Dummy> m_Dummy;
};
Test::Test() {
auto data = // generate some data here
m_Dummy = std::make_unique<Dummy>(data);
}
What happens:
The assignment operator on m_Dummy calls unique_ptr::reset,
which calls delete on the pointer,
which calls the m_Dummy destructor,
and running the destructor creates a segfault because it was never initialized.
The correct way would be to initialize it in the constructor initialization list.
But then I would not be able to pass in the data I want.
Test::Test() : m_Dummy{std::make_unique<Dummy>(data)} { // Don't have time to generate data
}
I am not sure how to make this cleaner.
My current idea is to change Dummy to have default constructor then an initialize function which takes the data.
It feels wrong though.
Is there a cleaner way to handle this?
What is normally done with smart pointers that need parameters and also need to be a class member?
Thanks,
Nathan
Edit:
From the answer below perhaps there is a completely different problem somewhere in my code causing this.
This is the callstack from the debugger just before the segfault is thrown.
Dummy::~Dummy Dummy.cpp:24
std::default_delete<Dummy>::operator() unique_ptr.h:78
std::unique_ptr<Dummy, std::default_delete<Dummy> >::reset unique_ptr.h:371
std::unique_ptr<Dummy, std::default_delete<Dummy> >::operator= unique_ptr.h:278
Test::Test Test.cpp:42
std::make_unique<Test, int&, double, double> unique_ptr.h:821
World::World World.cpp:25
Application::Run Application.cpp:77
main main.cpp:10
__libc_start_main 0x00007fbd47bbdb97
_start 0x0000555e1df657ea
Edit2:
The problem was that in the process of creating my data I was corrupting my memory and Dummy just happened to be the victim.
My original proposal to create the unique_ptr works now.
Thanks
What happens:
The assignment operator on m_Dummy calls unique_ptr::reset,
which calls delete on the pointer,
which calls the m_Dummy destructor,
and running the destructor creates a segfault because it was never initialized.
That is NOT what happens under normal conditions.
m_Dummy is not initialized explicitly in the Test constructor, so it gets implicitly default-constructed instead, and its default constructor sets its held pointer to nullptr.
When a unique_ptr holds nullptr, reset() is a no-op. It is perfectly safe to assign to a unique_ptr that is holding nullptr.
Even if reset() were not a no-op, it is perfectly safe to call delete on a nullptr.
That said, the ONLY way Dummy's destructor could be getting called when assigning to m_Dummy is when m_Dummy is not holding a nullptr. For that to happen in the Test constructor you have shown, m_Dummy would have to be in an invalid state, either because:
the Test constructor was called on invalid memory (unlikely, unless you are misusing placement-new)
your code to initialize data, or even the Dummy constructor itself, is corrupting random memory, and m_Dummy is an unwitting victim of that corruption (more likely).
Create a static method to generate the data?
class Test {
public:
Test();
private:
static Dummy makeConstructionData()
{
return Dummy();
}
std::unique_ptr<Dummy> m_Dummy;
};
Then later on you can do:
Test::Test() : m_Dummy{std::make_unique<Dummy>(makeConstructionData())} {
}
Related
There's a project focusing on using C++ 98 without additional dependencies, but it needs to maintain dynamically allocated memory. Smart pointers are not available, so code to manually clean things up has been added. The approach is to explicitly set variables to NULL in the CTOR, read some data during which memory might be allocated dynamically, catch any occurring exception and clean memory up as necessary by manually calling the DTOR. That needs to implement freeing memory anyway in case everything succeeded and has simply been enhanced by safeguards to check if memory has been allocated at all or not.
The following is the most relevant available code for this question:
default_endian_expr_exception_t::doc_t::doc_t(kaitai::kstream* p__io, default_endian_expr_exception_t* p__parent, default_endian_expr_exception_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_main = 0;
try {
_read();
} catch(...) {
this->~doc_t();
throw;
}
}
void default_endian_expr_exception_t::doc_t::_read() {
m_indicator = m__io->read_bytes(2);
m_main = new main_obj_t(m__io, this, m__root);
}
default_endian_expr_exception_t::doc_t::~doc_t() {
if (m_main) {
delete m_main; m_main = 0;
}
}
The most relevant part of the header is the following:
class doc_t : public kaitai::kstruct {
public:
doc_t(kaitai::kstream* p__io, default_endian_expr_exception_t* p__parent = 0, default_endian_expr_exception_t* p__root = 0);
private:
void _read();
public:
~doc_t();
private:
std::string m_indicator;
main_obj_t* m_main;
default_endian_expr_exception_t* m__root;
default_endian_expr_exception_t* m__parent;
};
The code is tested in three different environments, clang3.5_linux, clang7.3_osx and msvc141_windows_x64, to explicitly throw exceptions when reading data and if it leaks memory under those conditions. The problem is that this triggers SIGABRT on CLANG 3.5 for Linux only. The most interesting stack frames are the following:
<frame>
<ip>0x577636E</ip>
<obj>/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19</obj>
<fn>std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()</fn>
</frame>
<frame>
<ip>0x5ECFB4</ip>
<obj>/home/travis/build/kaitai-io/ci_targets/compiled/cpp_stl_98/bin/ks_tests</obj>
<fn>default_endian_expr_exception_t::doc_t::doc_t(kaitai::kstream*, default_endian_expr_exception_t*, default_endian_expr_exception_t*)</fn>
<dir>/home/travis/build/kaitai-io/ci_targets/tests/compiled/cpp_stl_98</dir>
<file>default_endian_expr_exception.cpp</file>
<line>51</line>
</frame>
[...]
<frame>
<ip>0x577636E</ip>
<obj>/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19</obj>
<fn>std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()</fn>
</frame>
<frame>
<ip>0x5ED17E</ip>
<obj>/home/travis/build/kaitai-io/ci_targets/compiled/cpp_stl_98/bin/ks_tests</obj>
<fn>default_endian_expr_exception_t::doc_t::~doc_t()</fn>
<dir>/home/travis/build/kaitai-io/ci_targets/tests/compiled/cpp_stl_98</dir>
<file>default_endian_expr_exception.cpp</file>
<line>62</line>
</frame>
The lines 51 one and 62 are the last lines of the CTOR and DTOR as provided above, so really the closing brackets. This looks like some added code by the compiler is simply trying to free the maintained std::string two times, once in the DTOR and an additional time in the CTOR, most likely only when throwing an exception.
Is this analysis correct at all?
And if so, is this expected behvaiour of C++ in general or this concrete compiler only? I wonder because the other compilers don't SIGABRT, even though the code is the same for all. Does this mean that different compilers clean non-pointers like std::string up differently? How does one know how each compiler behaves?
Looking at what the C++-standard says, I would have expected that the std::string being freed only by the CTOR because of the exception:
C++11 15.2 Constructors and destructors (2)
An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution.
The destruction is NOT terminated by an exception in this case, only the construction. But because the DTOR is a DTOR, it's designed to automatically clean things up as well? And if so, in general with all compilers or only this one?
Is calling a DTOR manually reliable at all?
According to my research, calling a DTOR manually shouldn't be too bad. Is that a wrong expression and it's a big no-go because of the things I see right now? I had the impression that if a DTOR is called manually, it simply needs to be compatible to be called this way. Which the above should be from my understanding. It only fails because of aut-generated code by the compiler I wasn't aware of.
How to fix this?
Instead of calling the DTOR manually and trigger the automatically generated code, one should simply use a custom cleanUp-function freeing memory and setting pointers to NULL? It should be safe to call that in the CTOR in case of an exception and always in the DTOR, correct? Or is there some way to keep calling the DTOR in a compatible way for all compilers?
Thanks!
Here's a simplified example that resembles your case, and makes the behavior obvious:
#include <iostream>
struct S {
S() { std::cout << "S constructed\n";}
~S() { std::cout << "S destroyed\n";}
};
class Throws {
S s;
public:
Throws() {
try {
throw 42;
} catch (int) {
this->~Throws();
throw;
}
}
};
int main() {
try {
Throws t;
} catch (int) {}
}
Output:
S constructed
S destroyed
S destroyed
Demo with clang, demo with gcc.
The example exhibits undefined behavior, by destroying the same S instance twice. Since the destructor doesn't do much, and in particular doesn't access this, the undefined behavior manifests itself by actually running the destructor twice successfully, so it can be easily observed in action.
Apparently, the OP has doubts that a destructor is supposed to actually destroy the object, together with all its members and base classes. To assuage those doubts, here's the relevant quote from the standard:
[class.dtor]/14 After executing the body of the destructor and destroying any objects with automatic storage duration allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s non-virtual direct base classes and, if X is the most derived class (11.10.2), its destructor calls the destructors for X’s virtual base classes...
Once the destructor is called, the object ceases to be (leaving you with uninitialized memory). This means that destructors may omit "finalizing" memory writes, such as setting a pointer to zero (the object ceases to be, so its value cannot ever be read). It also means that basically any further operation on that object is UB.
People assume some leeway on destroying *this, if the this pointer is not used in any way anymore. This is not the case in your example, as the destructor is called twice.
I am aware of exactly one case in which calling the destructor manually is correct and one where it is mostly-correct: When the object was created with placement new (in which case there will be no operation that automatically calls the destructor). The mostly-correct case is when destroying the object is immediately followed by re-initializing the object via a call to placement-new at the very same location.
As to your second question: Why do you want to explicitly call the destructor anyway? As far as I can see, your code should work just fine without all the contortions:
default_endian_expr_exception_t::doc_t::doc_t(kaitai::kstream* p__io, default_endian_expr_exception_t* p__parent, default_endian_expr_exception_t* p__root)
: kaitai::kstruct(p__io), m__parent(p__parent), m__root(p__root), m_main() {
_read();
}
The object is initialized to a valid state before the user-provided constructor is run. If _read throws an exception that should still be the case (otherwise fix _read!) and therefore the implicit destructor call should clean up everything nicely.
Let's say there's a simple class hierarchy, and a state object that uses the derived class;
struct base_class {
int Value;
base_class() { this->Value = 1; }
virtual void Func() { printf("Base\n"); };
};
struct derived_class : base_class {
int Value;
derived_class() { this->Value = 2; }
void Func() { printf("Derived\n"); }
};
struct state {
int a,b,c;
derived_class Object;
};
Now, let's assume that there's an allocator, that is not aware of the types and just returns 0-initialized allocated block memory of required size.
state *State = (state *)Allocate(sizeof(state));
And now, to properly initialize the vtable pointers we must construct the object.
I've seen it done with placement new operator. And it does indeed seem to work.
However, I'm interested why if I construct the state object like this
*State = {};
The State is initialized perfectly, I see the values set to 1 and 2. But the _vfprt is 0. Even if I step into the constructor, the this pointer seems to have everything correctly set up, _vfprt points to the correct method and all.
But when I return from the constructor, the _vfprt fails to get copied itho the State object. Everything else is there. But the _vfprt is 0;
So I'm just wondering if there's a special magical copy constructor that's invoked whenever new() operator is used. And if there is one, how can I use it.
I use this kind of initialization everywhere in my app, and honestly adding placement new everywhere just to support one small class is a pain in the butt. the {} call is much cleaner (and shorter), and it makes the allocation calls so much easier. If it's not possible to make this work, I'm ok with that. I'm just confused as to why the vtable pointer is not copied back after we return from the constructor.
If anyone could explain why this happens, that would be great.
Thanks!
*state = {} is an assignment, not a construction. An assignment cannot change the dynamic type1 on an object. The virtual pointer only depends on the dynamic type of the object. So it is not necessary to copy the virtual pointer in an assignment.
In an assignment, the object on the left side is supposed to be within its life time. The placement new expression starts an object's life time, an assignment does not. In the assignment *state = {}, the compiler assumes that an object already exists at the memory location pointed to by state. So the compiler assumes that the virtual pointer has already been initialized. The placement new will construct the object, which initializes the virtual pointer.
1 The type of the most derived object, here it is state.
You invoke undefined behaviour! What you do by this assignment (*State = { };) is equivalent to: (*State).operator=({ });. As you notice, you call a function at an object of which the lifetime never began (just the same as if you did (*state).someFunction();), as no constructor ever was successfully called (well, wasn't called at all).
Peeking a bit under the hoods:
As your object is polymorphic, it receives a pointer to a virtual function table. Once an object is constructed, though, that pointer for sure won't change any more (objects cannot change their type as long as they live). So an assignment operator wouldn't need to change it! So the pointer to the vtable only gets installed inside the constructor, but as you never called one, it won't get installed at all.
This will apply for both the class itself (in given case without vtable, though) as well as for members or base classes (for all of which the assignment operators, which get called recursively, suffer from the same problem).
I have a class, the member variable is std::unique_ptr<double[]>. Initializing this variable is always done by make_unique<double[]>(size).
But I do not know how to write code for destructor for this member variable
I know std::unique_ptr has a method get_deleter(), but when I look at the docs, it just provide self-defined delete. I do some research about similiar examples. But all of them are about new className(), not make_unique().
the verison of cpp is c++17
My code
class test{
public:
test(int size) : size_{size}, arr_{make_unique<double[]>( size )} {}
~test(){
// how to destroy arr_
}
private:
int size_;
std::unique_ptr<double[]> arr_;
};
I do not know how to start. I know there is a keyword delete, but I think it is not useful in this case.
Do not write a destructor at all.
std::unique_ptr will do the cleanup for you correctly, and indeed that's the main point of using a unique_ptr in the first place.
Whenever possible, follow the Rule Of Zero: Use smart pointers and containers correctly, and don't declare any destructor, copy constructor, move constructor, copy assignment, or move assignment. (See the link for discussion of the main exception, declaring an interface's destructor virtual, and deleting or defaulting the rest.)
I do not know how to write code for destructor for this member variable
Nothing in particular needs to be written in order to destroy any member variable. All member objects as well as base class objects are destroyed by all destructors after the body of the destructor has been executed.
There is no need to do anything in the body of the destructor, and therefore the implicitly generated destructor is sufficient for the class.
We once had an interview with a very experienced C++ developer who couldn't answer the following question: is it necessary to call the base class destructor from the derived class destructor in C++?
Obviously the answer is no, C++ will call the base class destructor automagically anyway. But what if we attempt to do the call? As I see it the result will depend on whether the base class destructor can be called twice without invoking erroneous behavior.
For example in this case:
class BaseSafe {
public:
~BaseSafe()
{
}
private:
int data;
};
class DerivedSafe {
public:
~DerivedSafe()
{
BaseSafe::~BaseSafe();
}
};
everything will be fine - the BaseSafe destructor can be called twice safely and the program will run allright.
But in this case:
class BaseUnsafe {
public:
BaseUnsafe()
{
buffer = new char[100];
}
~BaseUnsafe ()
{
delete[] buffer;
}
private:
char* buffer;
};
class DerivedUnsafe {
public:
~DerivedUnsafe ()
{
BaseUnsafe::~BaseUnsafe();
}
};
the explicic call will run fine, but then the implicit (automagic) call to the destructor will trigger double-delete and undefined behavior.
Looks like it is easy to avoid the UB in the second case. Just set buffer to null pointer after delete[].
But will this help? I mean the destructor is expected to only be run once on a fully constructed object, so the optimizer could decide that setting buffer to null pointer makes no sense and eliminate that code exposing the program to double-delete.
Is the compiler allowed to do that?
Standard 12.4/14
Once a destructor is invoked for an
object, the object no longer exists;
the behavior is undefined if the
destructor is invoked for an object
whose lifetime has ended (3.8).
So I guess the compiler should be free to optimize away the setting of buffer to null since the object no longer exists after calling the destructor.
But even if the setting of the buffer to null wasn't removed by the compiler, it seems like calling the destructor twice would result in UB.
Calling the destructor converts an object into raw memory. You cannot destruct raw memory; this is undefined behaviour. The C++ compiler is entitled to do anything it wants. While it is unlikely that it will turn your computer in cottage cheese, it might deliberately trigger a slap-on-the-wrist SEGFAULT (at least in debug mode).
Yesterday I read some code of a colleague and came across this:
class a_class
{
public:
a_class() {...}
int some_method(int some_param) {...}
int value_1;
int value_2;
float value_3;
std::vector<some_other_class*> even_more_values;
/* and so on */
}
a_class a_instances[10];
void some_function()
{
do_stuff();
do_more_stuff();
memset(a_instances, 0, 10 * sizeof(a_class)); // <===== WTF?
}
Is that legal (the WTF line, not the public attributes)? To me it smells really, really bad...
The code ran fine when compiled with VC8, but it throws an "unexpected exception" when compiled with VC9 when calling a_instances[0].event_more_values.push_back(whatever), but when accessing any of the other members. Any insights?
EDIT: Changed the memset from memset(&a_instances... to memset(a_instances.... Thanks for pointing it out Eduard.
EDIT2: Removed the ctor's return type. Thanks litb.
Conclusion: Thanks folks, you confirmed my suspicion.
This is a widely accepted method for initialization for C structs.
In C++ it doesn't work ofcourse because you can't assume anything about vectors internal structure. Zeroing it out is very likely to leave it in an illegal state which is why your program crashes.
He uses memset on a non-POD class type. It's invalid, because C++ only allows it for the simplest cases: Where a class doesn't have a user declared constructor, destructor, no virtual functions and several more restrictions. An array of objects of it won't change that fact.
If he removes the vector he is fine with using memset on it though. One note though. Even if it isn't C++, it might still be valid for his compiler - because if the Standard says something has undefined behavior, implementations can do everything they want - including blessing such behavior and saying what happens. In his case, what happens is probably that you apply memset on it, and it would silently clear out any members of the vector. Possible pointers in it, that would point to the allocated memory, will now just contain zero, without it knowing that.
You can recommend him to clear it out using something like this:
...
for(size_t i=0; i < 10; i++)
objects[i].clear();
And write clear using something like:
void clear() {
a_object o;
o.swap(*this);
}
Swapping would just swap the vector of o with the one of *this, and clear out the other variables. Swapping a vector is especially cheap. He of course needs to write a swap function then, that swaps the vector (even_more_values.swap(that.even_more_values)) and the other variables.
I am not sure, but I think the memset would erase internal data of the vector.
When zeroing out a_instances, you also zero out the std_vector within. Which probably allocates a buffer when constructed. Now, when you try to push_back, it sees the pointer to the buffer being NULL (or some other internal member) so it throws an exception.
It's not legitimate if you ask. That's because you can't overload writing via pointers as you can overload assignment operators.
The worst part of it is that if the vector had anything in it, that memory is now lost because the constructor wasn't called.
NEVER over-write a C++ object. EVER. If it was a derived object (and I don't know the specifics of std::vector), this code also over-writes the object's vtable making it crashy as well as corrupted.
Whoever wrote this doesn't understand what objects are and needs you to explain what they are and how they work so that they don't make this kind of mistake in the future.
You shouldn't do memset on C++ objects, because it doesn't call the proper constructor or destructor.
Specifically in this case, the destructor of even_more_values member of all a_instances's elements is not called.
Actually, at least with the members that you listed (before /* and so on */), you don't need to call memset or create any special destructor or clear() function. All these members are deleted automatically by the default destructor.
You should implement a method 'clear' in your class
void clear()
{
value1=0;
value2=0;
value_3=0f;
even_more_values.clear();
}
What you have here might not crash, but it probably won't do what you want either! Zeroing out the vector won't call the destructor for each a_class instance. It will also overwrite the internal data for a_class.even_more_values (so if your push_back() is after the memset() you are likely to get an access violation).
I would do two things differently:
Use std::vector for your storage both in a_class and in some_function().
Write a destructor for a_class that cleans up properly
If you do this, the storage will be managed for you by the compiler automatically.
For instance:
class a_class
{
public:
a_class() {...}
~a_class() { /* make sure that even_more_values gets cleaned up properly */ }
int some_method(int some_param) {...}
int value_1;
int value_2;
float value_3;
std::vector<some_other_class*> even_more_values;
/* and so on */
}
void some_function()
{
std::vector<a_class> a_instances( 10 );
// Pass a_instances into these functions by reference rather than by using
// a global. This is re-entrant and more likely to be thread-safe.
do_stuff( a_instances );
do_more_stuff( a_instances );
// a_instances will be cleaned up automatically here. This also allows you some
// weak exception safety.
}
Remember that if even_more_values contains pointers to other objects, you will need to delete those objects in the destructor of a_class. If possible, even_more_values should contain the objects themselves rather than pointers to those objects (that way you may not have to write a destructor for a_class, the one the compiler provides for you may be sufficient).