class MyClass
{
};
void foo
{
MyClass arr[10];
}
I want to know the order of destruction of array objects when function returns.
I read it More Effective C++ about it and it says that destructor are invoked in reverse
order to that of constructor order as follows:
for(int i = 9 ; i >= 0 ;i--)
{
arr[i].MyClass::~MyClass();
}
Can anybody tell me the reason for it ?
That's continuation of reverse order of destructor invokation filosophy of C++. When stack-allocated objects are destroyed it is done in reverse order to facilitate RAII. Although that is not really necessary for array elements (they are all constructed with the default constructors and any order of construction/destruction would do) the same is done with them just for consistency.
The information you're referring to in More Effective C++ applies to objects that contain a couple of other object, as in this case:
class Foo {
private:
Bar bar_1;
Bar bar_2;
public:
Foo() : bar_1(), bar_2() {}
};
In the above example, you'll have bar_1 constructed first, followed by bar_2. When an object of class Foo then gets destroyed, bar_2 gets destroyed first, then bar_1. That is what Scott Meyers is referring to.
From the point of view of the class, an array of bars would be another object that the compiler needs to destroy, so the order of destruction affects when the array gets destructed in the context of the other objects in the class.
As to which order the elements of an array get destroyed, I wouldn't be too surprised if that is implementation dependent. You'll also have optimisation playing a role here (for example, a POD array can be destroyed just by freeing its memory, as can be an object that is solely composed of PODs). All of the above can affect the order in which the elements of an array will be destroyed.
I'd be interested to see why you do need to know the order in which the array elements are destroyed (apart from technical curiosity, which would be a valid reason IMHO). If it is because there are dependencies between the elements of the array, I think the data structure might need reviewing.
Any order would do, really. The two obvious choices are of course in order or in reverse order. However, in this case none of the compiler makers thought it would be worthwhile to leave this implementation-dependent. Hence, the choice was made to mandate the reverse order (as sharptooth stated, extending the usual LIFO behavior)
You don't cite which page in Meyer's book you are referring to, but I agree with Timo Geusch that the explanation sounds like it is referring to the that constructors and destructors are invoked according to the inheritance.
For an array of object instances, the order that objects are destroyed is the inverse of the order of construction. This is easy to verify, as the following code shows. A class variable keeps track of the total number of instances created, and a data member for each object keeps track of its own number. The constructor and destructor print a message, so when run we can see exactly what happens and when.
The test code prints out the count of objects from 0 to 9 when constructing, then from 9 down to 0 when destructing the instances. (This was tested with g++-4.2 on Mac OS X.)
#include <iostream>
class MyClass
{
public:
MyClass()
{
mCounter = kInstanceCount++;
std::cout << "+++ MyClass() " << mCounter << std::endl;
}
~MyClass()
{
std::cout << "--- MyClass() " << mCounter << std::endl;
}
private:
unsigned mCounter;
static unsigned kInstanceCount;
};
unsigned MyClass::kInstanceCount = 0;
int main()
{
MyClass arr[10];
return 0;
}
You would need to check the C++ Standard, as I'm not 100% sure that this is not an implementation detail (as is often the case), in which case you don't want to rely on this behaviour.
Note also that it is not so common to create a stack-based array of actual object instances. You are more likely to be using std::vector, or maybe using smart pointers to heap-allocated objects.
Perhaps the order in which the objects that make up the array are placed on the stack. Anyway, except curiosity, I can think of no reason to worry about the destruction order.
Related
I've been trying not to initialize memory when I don't need to, and am using malloc arrays to do so:
This is what I've run:
#include <iostream>
struct test
{
int num = 3;
test() { std::cout << "Init\n"; }
~test() { std::cout << "Destroyed: " << num << "\n"; }
};
int main()
{
test* array = (test*)malloc(3 * sizeof(test));
for (int i = 0; i < 3; i += 1)
{
std::cout << array[i].num << "\n";
array[i].num = i;
//new(array + i) i; placement new is not being used
std::cout << array[i].num << "\n";
}
for (int i = 0; i < 3; i += 1)
{
(array + i)->~test();
}
free(array);
return 0;
}
Which outputs:
0 ->- 0
0 ->- 1
0 ->- 2
Destroyed: 0
Destroyed: 1
Destroyed: 2
Despite not having constructed the array indices. Is this "healthy"? That is to say, can I simply treat the destructor as "just a function"?
(besides the fact that the destructor has implicit knowledge of where the data members are located relative to the pointer I specified)
Just to specify: I'm not looking for warnings on the proper usage of c++. I would simply like to know if there's things I should be wary of when using this no-constructor method.
(footnote: the reason I don't wanna use constructors is because many times, memory simply does not need to be initialized and doing so is slow)
No, this is undefined behaviour. An object's lifetime starts after the call to a constructor is completed, hence if a constructor is never called, the object technically never exists.
This likely "seems" to behave correctly in your example because your struct is trivial (int::~int is a no-op).
You are also leaking memory (destructors destroy the given object, but the original memory allocated via malloc still needs to be freed).
Edit: You might want to look at this question as well, as this is an extremely similar situation, simply using stack allocation instead of malloc. This gives some of the actual quotes from the standard around object lifetime and construction.
I'll add this as well: in the case where you don't use placement new and it clearly is required (e.g. struct contains some container class or a vtable, etc.) you are going to run into real trouble. In this case, omitting the placement-new call is almost certainly going to gain you 0 performance benefit for very fragile code - either way, it's just not a good idea.
Yes, the destructor is nothing more than a function. You can call it at any time. However, calling it without a matching constructor is a bad idea.
So the rule is: If you did not initialize memory as a specific type, you may not interpret and use that memory as an object of that type; otherwise it is undefined behavior. (with char and unsigned char as exceptions).
Let us do a line by line analysis of your code.
test* array = (test*)malloc(3 * sizeof(test));
This line initializes a pointer scalar array using a memory address provided by the system. Note that the memory is not initialized for any kind of type. This means you should not treat these memory as any object (even as scalars like int, let aside your test class type).
Later, you wrote:
std::cout << array[i].num << "\n";
This uses the memory as test type, which violates the rule stated above, leading to undefined behavior.
And later:
(array + i)->~test();
You used the memory a test type again! Calling destructor also uses the object ! This is also UB.
In your case you are lucky that nothing harmful happens and you get something reasonable. However UBs are solely dependent on your compiler's implementation. It can even decide to format your disk and that's still standard-conforming.
That is to say, can I simply treat the destructor as "just a function"?
No. While it is like other functions in many ways, there are some special features of the destructor. These boil down to a pattern similar to manual memory management. Just as memory allocation and deallocation need to come in pairs, so do construction and destruction. If you skip one, skip the other. If you call one, call the other. If you insist upon manual memory management, the tools for construction and destruction are placement new and explicitly calling the destructor. (Code that uses new and delete combine allocation and construction into one step, while destruction and deallocation are combined into the other.)
Do not skip the constructor for an object that will be used. This is undefined behavior. Furthermore, the less trivial the constructor, the more likely that something will go wildly wrong if you skip it. That is, as you save more, you break more. Skipping the constructor for a used object is not a way to be more efficient — it is a way to write broken code. Inefficient, correct code trumps efficient code that does not work.
One bit of discouragement: this sort of low-level management can become a big investment of time. Only go this route if there is a realistic chance of a performance payback. Do not complicate your code with optimizations simply for the sake of optimizing. Also consider simpler alternatives that might get similar results with less code overhead. Perhaps a constructor that performs no initializations other than somehow flagging the object as not initialized? (Details and feasibility depend on the class involved, hence extend outside the scope of this question.)
One bit of encouragement: If you think about the standard library, you should realize that your goal is achievable. I would present vector::reserve as an example of something that can allocate memory without initializing it.
You currently have UB as you access field from non-existing object.
You might let field uninitialized by doing a constructor noop. compiler might then easily doing no initialization, for example:
struct test
{
int num; // no = 3
test() { std::cout << "Init\n"; } // num not initalized
~test() { std::cout << "Destroyed: " << num << "\n"; }
};
Demo
For readability, you should probably wrap it in dedicated class, something like:
struct uninitialized_tag {};
struct uninitializable_int
{
uninitializable_int(uninitialized_tag) {} // No initalization
uninitializable_int(int num) : num(num) {}
int num;
};
Demo
This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 8 years ago.
I read that destructors need to be defined when we have pointer members and when we define a base class, but I am not sure if I completely understand. One of the things I am not sure about is whether or not defining a default constructor is useless or not, since we are always given a default constructor by default. Also, I am not sure if we need to define default constructor to implement the RAII principle (do we just need to put resource allocation in a constructor and not define any destructor?).
class A
{
public:
~Account()
{
delete [] brandname;
delete b;
//do we need to define it?
};
something(){} =0; //virtual function (reason #1: base class)
private:
char *brandname; //c-style string, which is a pointer member (reason #2: has a pointer member)
B* b; //instance of class B, which is a pointer member (reason #2)
vector<B*> vec; //what about this?
}
class B: public A
{
public something()
{
cout << "nothing" << endl;
}
//in all other cases we don't need to define the destructor, nor declare it?
}
The rule of Three and The Rule of Zero
The good ol' way of handling resources was with the Rule of Three (now Rule of Five due to move semantic), but recently another rule is taking over: the Rule of Zero.
The idea, but you should really read the article, is that resource management should be left to other specific classes.
On this regard the standard library provides a nice set of tools like: std::vector, std::string, std::unique_ptr and std::shared_ptr, effectively removing the need for custom destructors, move/copy constructors, move/copy assignment and default constructors.
How to apply it to your code
In your code you have a lot of different resources, and this makes for a great example.
The string
If you notice brandname is effectively a "dynamic string", the standard library not only saves you from C-style string, but automatically manages the memory of the string with std::string.
The dynamically allocated B
The second resource appears to be a dynamically allocated B. If you are dynamically allocating for other reasons other than "I want an optional member" you should definitely use std::unique_ptr that will take care of the resource (deallocating when appropriate) automatically. On the other hand, if you want it to be an optional member you can use std::optional instead.
The collection of Bs
The last resource is just an array of Bs. That is easily managed with an std::vector. The standard library allows you to choose from a variety of different containers for your different needs; Just to mention some of them: std::deque, std::list and std::array.
Conclusion
To add all the suggestions up, you would end up with:
class A {
private:
std::string brandname;
std::unique_ptr<B> b;
std::vector<B> vec;
public:
virtual void something(){} = 0;
};
Which is both safe and readable.
As #nonsensickle points out, the questions is too broad... so I'm gonna try to tackle it with everything I know...
The first reason to re define the destructor would be in The Rule of Three which is on part the item 6 in Scott Meyers Effective C++ but not entirely. The rule of three says that if you re defined the destructor, copy constructor, or copy assignment operations then that means you should rewrite all three of them. The reason is that if you had to rewrite your own version for one, then the compiler defaults will no longer be valid for the rest.
Another example would be the one pointed out by Scott Meyers in Effective C++
When you try to delete a derived class object through a base class pointer and the base class has a non virtual destructor, the results are undefined.
And then he continues
If a class does not contain any virtual functions, that is often an indication that it is not meant to be used as a base class. When a class is not intended to be used as a base class, making the destructor virtual is usually a bad idea.
His conclusion on destructors for virtual is
The bottom line is that gratuitously declaring all destructors virtual is just as wrong as never declaring them virtual. In fact, many people summarize the situation this way: declare a virtual destructor in a class if and only if that class contains at least one virtual function.
And if it is not a Rule Of three case, then maybe you have a pointer member inside your object, and maybe you allocated memory to it inside your object, then, you need to manage that memory in the destructor, this is item 6 on his book
Be sure to check out #Jefffrey's answer on the Rule of Zero
There are precisely two things that necessitate defining a destructor:
When your object gets destructed, you need to perform some action other than destructing all class members.
The vast majority of these actions once was freeing memory, with the RAII principle, these actions have moved into the destructors of the RAII containers, which the compiler takes care of calling. But these actions can be anything, like closing a file, or writing some data to a log, or ... . If you strictly follow the RAII principle, you will write RAII containers for all these other actions, so that only RAII containers have destructors defined.
When you need to destruct objects through a base class pointer.
When you need to do this, you must define the destructor to be virtual within the base class. Otherwise, your derived destructors won't get called, independent of whether they are defined or not, and whether they are virtual or not. Here is an example:
#include <iostream>
class Foo {
public:
~Foo() {
std::cerr << "Foo::~Foo()\n";
};
};
class Bar : public Foo {
public:
~Bar() {
std::cerr << "Bar::~Bar()\n";
};
};
int main() {
Foo* bar = new Bar();
delete bar;
}
This program only prints Foo::~Foo(), the destructor of Bar is not called. There is no warning or error message. Only partially destructed objects, with all the consequences. So make sure you spot this condition yourself when it arises (or make a point to add virtual ~Foo() = default; to each and every nonderived class you define.
If none of these two conditions are met, you don't need to define a destructor, the default constructor will suffice.
Now to your example code:
When your member is a pointer to something (either as a pointer or a reference), the compiler does not know ...
... whether there are other pointers to this object.
... whether the pointer points to one object, or to an array.
Hence, the compiler can't deduce whether, or how to destruct whatever the pointer points to. So the default destructor never destructs anything behind a pointer.
This applies both to brandname and to b. Consequently, you need a destructor, because you need to do the deallocation yourself. Alternatively, you can use RAII containers for them (std::string, and a smart pointer variant).
This reasoning does not apply to vec because this variable directly includes a std::vector<> within the objects. Consequently, the compiler knows that vec must be destructed, which in turn will destruct all its elements (it's a RAII container, after all).
If you dynamically allocate memory, and you want this memory to be deallocated only when the object itself is "terminated", then you need to have a destructor.
The object can be "terminated" in two ways:
If it was statically allocated, then it is "terminated" implicitly (by the compiler).
If it was dynamically allocated, then it is "terminated" explicitly (by calling delete).
When "terminated" explicitly using a pointer of a base-class type, the destructor has to be virtual.
We know that if a destructor is not provided, the compiler will generate one.
This means that anything beyond simple cleanup, such as primitive types, will require a destructor.
In many cases, dynamic allocation or resource acquisition during construction, has a clean up phase. For example, dynamically allocated memory may need to be deleted.
If the class represents a hardware element, the element may need to be turned off, or placed into a safe state.
Containers may need to delete all of their elements.
In summary, if the class acquires resources or requires specialized cleanup (let's say in a determined order), there should be destructor.
I encounter many times with code where std::vector::clear() of class member of type std::vector is called in constructor and destructor.
I don't see why it's required:
constructor - the class member of type std::vector is empty by default, so no need to call clear().
destructor - the class member of type std::vector will be destroyed as part of standard destruction of object contnaining it. As part of vector destruction all value objects containied in it will be destroyed (if it heap allocated pointers to memory, they should be deleted "manually"), so again no need to call clear().
Do I miss something?
No, you're not missing anything. I suspect this is (harmless) voodoo programming, sort of like setting a pointer to null after freeing it, or randomly calling repaint/revalidate in GUI code. The programmer remembers that it helped with some sort of bug in the past and now adds it unnecessarily "just in case". Who knows, maybe it'll help. Voodoo.
From the sound of things, the people who wrote that code were the ones who missed something. The only time it would make sense to call clear() in a ctor or dtor would be in the middle of some other code. For example, a ctor might read in some data, process it, then read in more data. In such a case, it's probably faster to use a single container for the data as you read it in, and clear it each time, than to create a new container every iteration.
It's COMPLETELY unnessecary to clear the contents of an stl container in a constructor
It's unnessecary to clear the contents of an stl container in a destructor UNLESS the container contains a pointer. If the pointer has been created using new, it still needs to be deleted first. After that it still will not be nessecary to .clear the container.
Consider this :
#define BOOST_TEST_MODULE StlContainers
#define BOOST_LIB_DIAGNOSTIC
#include <boost/test/unit_test.hpp>
#include <boost/assign.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/assign/std/vector.hpp>
#include <vector>
using namespace boost::assign;
using namespace std;
const vector<int> my_ints_vector = list_of(0)(1)(1)(2)(3)(5)(8)(13)(21)(34);
struct ScopedStruct1
{
ScopedStruct1(const vector<int> & v) : m_v(v) {}
~ScopedStruct1() {}
private :
vector<int> m_v;
};
class A
{
public :
A(int i) : m_i(i) {}
~A() {}
private :
int m_i;
};
struct ScopedStruct2
{
ScopedStruct2() {}
~ScopedStruct2() { for(vector<A*>::iterator it = m_v.begin(); it != m_v.end(); ++it) delete *it; }
vector<A*> m_v;
};
struct ScopedStruct3
{
ScopedStruct3() {}
~ScopedStruct3() { /* no deletion */ }
vector<A*> m_v;
};
BOOST_AUTO_TEST_CASE(StlContainer_storing_something_simple)
{
ScopedStruct1 str(my_ints_vector);
}
BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_with_delete)
{
ScopedStruct2 str;
for(int i = 0; i < 10; i++)
str.m_v.push_back(new A(i));
}
BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_without_delete)
{
ScopedStruct3 str;
for(int i = 0; i < 10; i++)
str.m_v.push_back(new A(i));
}
Using boost's unit_test framework I've created 3 test cases. The unit_test framework is greate because it tracks memory leaks.
You'll notice that the 1st and 2nd test case don't generate memory leaks, but the 3rd case does because the vector's contents are not deleted.
Nope, you're right. Unless there is some additional business in the constructor (or constructor of the base classes) that require that, but the chances are very low...
Later edit
In case of destructor, one of the most common mistakes I saw is that some people assume that clear method will also call delete for vectors of pointers (vector), which, of course, it is not the case
The only case I can think of where it would be useful is where the order of destruction matters and the destructor wants to ensure that the objects in the vector are destroyed before something else.
Of course, it is better to structure code to not require that; however, it is a conceivable reason.
Despite what what said so far, there's at least one scenario when an explicit call to clear in the destructor might be necessary.
Imagine the situation when the object being destroyed has several member subobjects, which require some specific order of destruction, i.e. the subobjects somehow depend on each other, and an incorrect order of their destruction will lead to undesirable results. As you probably know, the order of member subobject destruction (as well as member initialization) is determined by the order of members' declararion in class definition. So, one way to achive the proper order of destruction is to arrange the member declarations accordingly. However, firstly, this is not a very good solution maintenance-wise. Secondly, the desired order of destruction might depend on some run-time conditions. Thirdly, the desired order of destruction might contradict the desired oreder of initialization. This all means that it might not be possible (or wise) to command the proper order of destruction by re-arranging the declarations.
A reasonable approach in this case might be to clean-up some critical member subobjects manually, by calling their clean methods or such, until the destruction order dependency "disappears". I would guess, that maybe the code that you saw was trying to resolve to ordering problem by calling clean on a strategically selected vector subobject.
As for calling clean in constructor... I have no idea why anyone would do something like that.
Of course one has to call clear() or resize(0) or the equivalent say (std::_Destroy_range(...) in the destructor before deallocating.
Deallocation is done via allocator::deallocate which does NOT run any destructors. It just frees the memory.
clear() is equivalent to resize(0) which runs destructors on the first size() things in the allocated buffer
NOT just allocated pointers, file handles, held mutexes, all other recoverable resources held by the object. The destructors MUST be run. Before instantiation the template doesn't know that the destructor is trivial. If the destructor is trivial, THEN its gets optimized out AFTER instantiation
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).
I'm not new to programming, but after working in Java I'm coming back to C++ and am a little confused about class variables that aren't pointers. Given the following code:
#include <iostream>
#include <map>
using namespace std;
class Foo {
public:
Foo() {
bars[0] = new Bar;
bars[0]->id = 5;
}
~Foo() { }
struct Bar {
int id;
};
void set_bars(map<int,Bar*>& b) {
bars = b;
}
void hello() {
cout << bars[0]->id << endl;
}
protected:
map<int,Bar*> bars;
};
int main() {
Foo foo;
foo.hello();
map<int,Foo::Bar*> testbars;
testbars[0] = new Foo::Bar;
testbars[0]->id = 10;
foo.set_bars(testbars);
foo.hello();
return(0);
}
I get the expected output of 5 & 10. However, my lack of understanding about references and pointers and such in C++ make me wonder if this will actually work in the wild, or if once testbars goes out of scope it will barf. Of course, here, testbars will not go out of scope before the program ends, but what if it were created in another class function as a function variable? Anyway, I guess my main question is would it better/safer for me to create the bars class variable as a pointer to the map map?
Anyway, I guess my main question is
would it better/safer for me to create
the bars class variable as a pointer
to the map map?
No. C++ is nothing like Java in this and may other respects. If you find yourself using pointers and allocating new'd objects to them a lot, you are probably doing something wrong. To learn the right way to do things, I suggest getting hold of a copy of Accelerated C++ by Koenig & Moo,
The member variable bars is a separate instance of a "dictionary"-like/associative array class. So when it is assigned to in set_bars, the contents of the parameter b are copied into bars. So there is no need to worry about the relative lifetimes of foo and testbars, as they are independent "value-like" entites.
You have more of a problem with the lifetimes of the Bar objects, which are currently never going to be deleted. If you add code somewhere to delete them, then you will introduce a further problem because you are copying the addresses of Bar objects (rather than the objects themselves), so you have the same object pointed to by two different maps. Once the object is deleted, the other map will continue to refer to it. This is the kind of thing that you should avoid like the plague in C++! Naked pointers to objects allocated with new are a disaster waiting to happen.
References (declared with &) are not different from pointers with regard to object lifetimes. To allow you to refer to the same object from two places, you can use either pointers or references, but this will still leave you with the problem of deallocation.
You can get some way toward solving the deallocation problem by using a class like shared_ptr, which should be included with any up-to-date C++ environment (in std::tr1). But then you may hit problems with cyclical pointer networks (A points to B and B points to A, for example), which will not be automatically cleaned up.
For every new you need a corresponding delete.
If you try and reference the memory after you call delete - where ever that is - then the program will indeed "barf".
If you don't then you will be fine, it's that simple.
You should design your classes so that ownership of memory is explicit, and that you KNOW that for every allocation you are doing an equal deallocation.
Never assume another class/container will delete memory you allocated.
Hope this helps.
In the code below you can pass map of Bars and then will be able to modify Bars outside of the class.
But. But unless you call set_bars again.
It is better when one object is responsible for creation and deletion of Bars. Which is not true in your case.
If you want you can use boost::shared_ptr< Bars > instead of Bars*. That will be more Java like behavior.
class Foo {
public:
Foo() {
bars[0] = new Bar;
bars[0]->id = 5;
}
~Foo() { freeBarsMemory(); }
struct Bar {
int id;
};
typedef std::map<int,Bar*> BarsList;
void set_bars(const BarsList& b) {
freeBarsMemory();
bars = b;
}
void hello() {
std::cout << bars[0]->id << std::endl;
}
protected:
BarsList bars;
void freeBarsMemory()
{
BarsList::const_iterator it = bars.begin();
BarsList::const_iterator end = bars.end();
for (; it != end; ++it)
delete it->second;
bars.clear();
}
};
I'm not new to programming, but after working in Java I'm coming back to C++ and am a little confused about class variables that aren't pointers.
The confusion appears to come from a combination of data that is on the heap and data that is not necessarily on the heap. This is a common cause of confusion.
In the code you posted, bars is not a pointer. Since it's in class scope, it will exist until the object containing it (testbars) is destroyed. In this case testbars was created on the stack so it will be destroyed when it falls out of scope, regardless of how deeply nested that scope is. And when testbars is destroyed, subobjects of testbars (whether they are parent classes or objects contained within the testbars object) will have their destructors run at that exact moment in a well-defined order.
This is an extremely powerful aspect of C++. Imagine a class with a 10-line constructor that opens a network connection, allocates memory on the heap, and writes data to a file. Imagine that the class's destructor undoes all of that (closes the network connection, deallocates the memory on the heap, closes the file, etc.). Now imagine that creating an object of this class fails halfway through the constructor (say, the network connection is down). How can the program know which lines of the destructor will undo the parts of the constructor that succeeded? There is no general way to know this, so the destructor of that object is not run.
Now imagine a class that contains ten objects, and the constructor for each of those objects does one thing that must be rolled back (opens a network connection, allocates memory on the heap, writes data to a file, etc.) and the destructor for each of those objects includes the code necessary to roll back the action (closes the network connection, deallocates objects, closes the file, etc.). If only five objects are successfully created then only those five need to be destroyed, and their destructors will run at that exact moment in time.
If testbars had been created on the heap (via new) then it would only be destroyed when calling delete. In general it's much easier to use objects on the stack unless there is some reason for the object to outlast the scope it was created in.
Which brings me to Foo::bar. Foo::bars is a map that refers to objects on the heap. Well, it refers to pointers that, in this code example, refer to objects allocated on the heap (pointers can also refer to objects allocated on the stack). In the example you posted the objects these pointers refer to are never deleted, and because these objects are on the heap you're getting a (small) memory leak (which the operating system cleans up on program exit). According to the STL, std::maps like Foo::bar do not delete pointers they refer to when they are destroyed. Boost has a few solutions to this problem. In your case it's probably be easiest to simply not allocate these objects on the heap:
#include <iostream>
#include <map>
using std::map;
using std::cout;
class Foo {
public:
Foo() {
// normally you wouldn't use the parenthesis on the next line
// but we're creating an object without a name, so we need them
bars[0] = Bar();
bars[0].id = 5;
}
~Foo() { }
struct Bar {
int id;
};
void set_bars(map<int,Bar>& b) {
bars = b;
}
void hello() {
cout << bars[0].id << endl;
}
protected:
map<int,Bar> bars;
};
int main() {
Foo foo;
foo.hello();
map<int,Foo::Bar> testbars;
// create another nameless object
testbars[0] = Foo::Bar();
testbars[0].id = 10;
foo.set_bars(testbars);
foo.hello();
return 0;
}