So I have a class aCollection that has as its members a binary search tree and a hash table (for organizing data by different parameters). The way I designed the program to work was that aCollection has an add(aVendor& vendor) function that takes a dummy vendor object created in main and produces a pointer (using new) that points to a vendor object which then is passed to the bst and hash table's add functions.
Within the bst and hash table, they use new to create a node that contains a pointer to a vendor object and the requisite linking pointers (next, left, right, etc.)
In summary a dummy data object goes to aCollection::add(aVendor& vendor), and a pointer to aVendor object (with the data inside it) is sent to the bst and hash table which then store that pointer in their own node objects they declared using new.
My question is, how should I use delete to properly release the memory? The bst and hash table share the pointers to the aVendor object that was passed to them and they each have their own nodes to delete. I know I need to call delete in the bst and hash table's remove functions (to delete their respective nodes) but how do I ensure that aVendor that is created in aCollection is deleted once and only once?
P.S. Is calling new in aCollection even necessary? I figure the pointer needs to stay allocated so the data always exists.
The code is a bit verbose so I made a quick illustration of what is going on.
----Solution----
Thanks to Ped7g's excellent explanation I figured out that since aCollection should be the function deleting the pointers I needed to keep track of the pointers to be deleted. Going by his/her suggestion I decided to use a std::list to add all the pointers added to the program to a list, then I designed a while loop in the destructor that iterates through those pointers and deletes them, thus preventing memory leaks stemming from aCollection, here is the code I wrote to do so.
20 //Constructor function
21
22 aCollection::aCollection()
23 {
24 //allocates one instance of a hash table and bst
25 hashTable = new hashFunctions;
26 bst = new aBst;
27
28 //Creates a list to track ptrs
29 trackList = std::list<aVendor*>();
30 return;
31 }
32
33 //Destructor
34 aCollection::~aCollection()
35 {
36 //Destroys hashTable and bst
37 delete hashTable;
38 delete bst;
39 //Deletes vendor pointer objects
40 while(!trackList.empty())
41 {
42 delete trackList.front();
43 trackList.pop_front();
44 }
45 return;
46 }
In the add function I used this line of code to add the pointers to the list
84 trackList.push_front(vendorPtr);
And finally this is how I declared the list as part of aCollection.h
43 list<aVendor*> trackList;
Try to follow the "everything belongs somewhere" principle.
Only the owner of information handles new/delete (better to say "avoids new/delete as much as possible"), other subjects don't care, if they receive a pointer, they assume it's live trough the whole time of their processing - because that's the owner responsibility, if it gave the pointer away, it should be aware how [long] it will be used outside, and adjust it's strategy of new/delete to fulfil those needs. So they don't delete it.
In your case it depends a lot, whether you remove vendors often from the structure, or you only add vendors to it. If you remove vendors only absolutely rarely and huge performance penalty is allowed then, you can have std::vector<aVendor> vendors; (1) in aCollection, giving the bs and hash nodes only iterator (or pointer/index) to the vendor. In case vendor is removed from vector, all bs/hash nodes have to updated with fresh iterator (pointer/index) = performance penalty upon remove. Well, actually inserting vendors will invalidate iterators and pointers too, only indices would survive, so use indices then - which also makes quite clear, how bs/hash nodes care about delete of vendor (you don't delete index, makes no sense).
If you remove vendors often, a std::list is better choice, as inserting/removing does not invalidate iterators, so all the copies of iterators in bs/hash nodes will remain correct.
Overall it looks like you wrote your own implementation of std::* containers... any particular reason for that? (it's a good learning exercise, and in very rare cases the performance decision, but in such case you would end with completely different design, because what you have is as horrible as std::list performance wise ... otherwise in production code it's usually much more efficient to stick with standard containers and design implementation around them, as they have reasonably OK-ish performance, and you don't have to implement them, only use).
(1) avoids new/delete completely. If that way is not practical for you (aVendor default constructor is costly), use std::vector<aVendor *> vendors; with manual new/delete upon insert/removal of vendor.
edit:
"how do I ensure that aVendor that is created in aCollection is deleted once and only once?"
Well, you delete it just once, in aCollection.
It's not clear from the question, what is your problem (maybe you are struggling with detection when a vendor is deleted in all nodes, and you want to release it from aCollection then too? That's completely different question, and would require much more architecture insight into the app algorithm, to see if there's some good place to detect "dangling" vendor no more used by any node, triggering delete in aCollection).
edit: how to new/delete example:
live example
#include <iostream>
#include <list>
#include <string>
class Vendor {
private:
std::string name;
public:
Vendor(const std::string & name) : name(name) {}
const std::string & getName() const { return name; }
};
// A bit pointless example how to handle naked new/delete.
class VendorList {
private:
std::list<Vendor *> vendors;
// usually with neatly designed classes the std::list<Vendor>
// would suffice, saving all the hassle with new/delete
// (that's why this example is a bit pointless)
// Also storing around iterators to internal list outside
// of VendorList class feels quite wrong, that's a code smell.
public:
~VendorList() {
std::cout << "~VendorList() destructor called.\n";
// release any remaining vendors from heap
auto vendorIterator = vendors.begin();
while (vendorIterator != vendors.end()) {
auto toRemove = vendorIterator++;
removeVendor(toRemove);
}
// release the (now invalid) pointers
vendors.clear();
// at this point, whoever still holds iterator
// to a vendor has a problem, it's invalid now.
}
// stores vendor into collection of vendors
// + data of vendor are allocated on heap by "new"
// returns iterator pointing to the newly added vendor
// (not a best choice for public API)
std::list<Vendor *>::iterator addVendor(const Vendor & vendor) {
Vendor * copyOnHeap = new Vendor(vendor);
std::cout << "VendorList: adding vendor: "
<< copyOnHeap->getName() << std::endl;
return vendors.insert(vendors.end(), copyOnHeap);
}
// removes particular vendor from the list
// to be used after the rest of application does not hold any iterator
void removeVendor(std::list<Vendor *>::iterator vendor_iterator) {
std::cout << "VendorList: releasing specific vendor: " <<
(*vendor_iterator)->getName() << std::endl;
// release the heap memory containing vendor's data
delete *vendor_iterator;
// remove the released pointer from list
vendors.erase(vendor_iterator);
// at this point, whoever still holds iterator
// to that vendor has a problem, it's invalid now.
}
const std::list<Vendor *> & get() const {
return vendors;
}
};
int main()
{
VendorList vlist;
vlist.addVendor(Vendor("v1"));
auto v2iterator = vlist.addVendor(Vendor("v2"));
vlist.removeVendor(v2iterator);
for (auto vendorPtr : vlist.get()) {
std::cout << "Vendor in list: " << vendorPtr->getName() << std::endl;
}
}
I'm not really happy about this example, as it feels wrong on many levels (like bad OOP design), but to make the API fit your purpose, the purpose would have to be known first.
So take this only as an example where new and delete belongs. (only once in VendorList, which is owner + responsible for managing these. Nobody else in app should use new/delete on Vendor, they should instead call the VendorList add/remove functions, and let the list to manage the implementation details (like where it is stored, and how many new/deletes are per vendor used).
Usually by designing your data classes lean, and avoiding naked pointers, you can avoid new/delete completely in C++ code. You can try to turn this example into std::list<Vendor> variant, the code will be simplified a lot (empty destructor code, as default would release the list), removal/insert only called on the list, etc.
You then handle the life cycle of data in memory by scope. Like in main you have VendorList vl;, as that instance of vendor list will be used trough whole life cycle of application. Or if you need it only during invoice processing, you can declare it inside processInvoices(), again as local variable, init it, and then forget about it. It will get released when you go out of scope of processInvoices().
Which will release all initialised Vendors, as they belong into VendorList. Etc..
So as long as you manage to design your classes with clear "belongs to" and "responsible for", you can get to a great lengths by using only local/member variables, not using new/delete/shared_ptr/etc... at all. The source will look almost like Java with GC, just shorter and faster, and you implicitly know when the release of particular data happens (when they go out of their scope).
Related
Everything below has to do with situations where a developer makes a custom C++ class (I have in mind something like OnlyKnowDemandAtRuntime below)... and there can be no way of knowing how many instances/objects "the user" will need during runtime.
Question 1: As a sanity check, is it fair to say that in Case One below, RAII is being used to manage the "dynamic" usage of OnlyKnowDemandAtRuntime?
Question 2: Is it correct to say that in case two, RAII isn't the first option that comes to mind simply because it is inconvenient (possibly a major understatement here) to hack up a way to wrap the nodes of a tree in an STL container? And, therefore, it is simpler to just use new and destructor/delete (or smart pointers) here, rather than scramble for a way to have the standard library manage memory for us. Note: Nothing here is a question about whether trees are often used in day to day work; rather, everything in this post is about the intuition behind the decision making one must do when using C++ to create objects at runtime. Note: Of course smart pointers are themselves part of the library, and of course they handle memory for us just as the library containers do... but for the purposes of this question I'm putting smart pointers and new on the same footing: Because my question is about the limits on the abilities of the STL containers to have more and more instances of something like OnlyKnowDemandAtRuntime inserted into them at runtime, while also being able to handle the relationships between said instances (without adding lots of logic to keep track of where things are in the container).
Question 3: If 1 and 2 are reasonable enough, then would a fair summary be this: [When a developer makes a custom class but doesn't know how many objects of it will be needed during runtime], either...
Wrap the objects in an STL container when the structure between said objects is "trackable" with the STL container being used (or perhaps trackable with the STL container being used plus some reasonably simple extra logic), or
Explicitly use the heap to build the objects with new and destructor/delete, or smart pointers, and manually build the structure "between" said objects (as in left_ and right_ of Case Two below).
Quick reminder, this isn't about whether we need to build trees in day to day work with C++. Also, (and I suppose this is already clear to anyone who would answer this question) this is not about "use the heap when an object is too big for the stack or when an object needs a lifetime beyond the scope in which it was created".
Case One:
// This is the class "for" which an unknown of objects will be created during runtime
class OnlyKnowDemandAtRuntime {
public:
OnlyKnowDemandAtRuntime(int num) : number_(num) {};
private:
int number_;
};
// This is the class where an a priori unknown number of `OnlyKnowDemandAtRuntime` objects are created at runtime.
class SomeOtherClass {
public:
void NeedAnotherOnlyKnownAtRuntime(int num) {
v_only_know_demand_at_runtime_.emplace_back(num);
}
private:
std::vector<OnlyKnowDemandAtRuntime> v_only_know_demand_at_runtime_;
}
Case Two:
// This is the class "for" which an unknown of objects will be created during runtime
class Node{
public:
Node(int value) : value_(value), left_(nullptr), right_(nullptr) {};
private:
int value_;
Node *left_;
Node *right_;
friend class Tree;
};
// This is the class where an a priori unknown number of `Node` objects are created at runtime.
class Tree {
public:
~Tree() { // Traverse tree and `delete` every `Node *` //}
void Insert(int value) {
Node *new_node = new Node(value);
ThisMethodPlacesNewNodeInTheAppropriateLeafPosition(new_node);
}
private:
Node *root;
}
Not to your literal questions but you might find this useful.
Smart pointers like std::unique_ptr are most basic RAII classes.
Using RAII is the only reasonably sane way to ensure exception safety.
In your particular example, I’d use std::unique_ptr<Node> specifically. With arbitrary graph that’d be more complicated ofc.
Also,
makes a custom C++ class but doesn't know how many objects of it will be needed during runtime.
That’s highly unspecific. It is important that you have a container (be it SomeOtherClass or Tree or whatever) that manages these objects. Otherwise, things may become really really complicated.
I'm trying to make map in which i will hold Teams as key and vector of Employees which are polymorphic as value. Some of the data will be loaded from file in future and the user will be able to add new teams and employees at any time.
This is the map that i came up with:
std::map<std::unique_ptr<Team>, std::unique_ptr<std::vector<std::unique_ptr<Employee>>>> teams;
And this is some test code where i tried to add new team and a member to it:
bool Company::addTeam(const std::string & projectName)
{
teams.emplace(std::unique_ptr<Team>(new Team(projectName)), std::unique_ptr<std::vector<std::unique_ptr<Employee>>>(new std::vector<std::unique_ptr<Employee>>()));
for (std::map<std::unique_ptr<Team>, std::unique_ptr<std::vector<std::unique_ptr<Employee>>>>::iterator it = teams.begin(); it != teams.end(); ++it) {
std::cout << it->first.get()->getProjectName();
it->second.get()->emplace_back(std::unique_ptr<Employee>(new Programmer("David", "Beckham", "9803268780", "Djoe Beckham", Employee::LevelThree, projectName)));
std::cout << "\n" << it->second.get()->at(0)->toString();
}
return false;
}
The code runs fine and im able to print the employee data but after closing the application it throws exception and visual studio open delete_scalar.cpp and triggers a breakpoint at this code :
void __CRTDECL operator delete(void* const block) noexcept
{
#ifdef _DEBUG
_free_dbg(block, _UNKNOWN_BLOCK); // break point
#else
free(block);
#endif
}
I'm trying to find out what i'm doing wrong and how to fix it. I have empty destructors for all Employees classes if that have something to do with the problem. If my idea looks very stupid and there is easier way to accomplish my goal please tell me. Thanks in advance.
Design-wise, one person can have multiple roles in different teams at the same time. It can be another class Role linking a person to a team. Neither team, no role conceptually own person objects though, so Role could use plain pointers to link Person with Team.
Your idea could work, if you would not have a problem within one of your class. Due to the missing parts, it's difficult to tell.
However, this is not the way to go !!
A map is meant to work with an index by value. A typical use case is to find back an item with an already existing key. Of course, you could use pointers as key, but the pointer would then act as a kind of id without any polymorphic operations.
On the other side, a unique_ptr is designed to ensure unique ownership. So only one unique copy of each pointer value exist. This makes it very difficult to use as map value:
auto team2 = make_unique<Team>();
teams [team2] = make_unique<vector<unique_ptr<Employee>>>(); // doesn't compile
The above code doesn't compile, because team2 is a unique_ptr and cannot be copied into the indexing parameter. Using it for searching or inserting an item, would require to move it:
teams [std::move(team2)] = make_unique<vector<unique_ptr<Employee>>>(); //compiles
assert (team2); // ouch
But once moved, the unique_ptr value is no longer in team2 which is now empty since the unique pointer is in the map key and it's unique. This means that you will never ever find back an added team.
Better alternatives ?
If you would want to really use a polymorphic pointer as a map key, you should at least use a shared_ptr, so that several copies can exist in your code. But I'd suggest that you use values only as a key
Now to the value part of the map. There is no benefit of making a unique_ptr of a vector: the vector itself is not polymorphic, and vectors are well designed for copying, moving and so on. Furthermore sizeof(vector<...>) is small even for very large vectors.
Use a vector<unique_ptr<Employee>> if you want the vector in the map to own the Employees, or vector<shared_ptr<Employee>> if you intend to share the content of the vector.
I'm developing a game server for a video game called Tibia.
Basically, there can be up to millions of objects, of which there can be up to thousands of deletes and re-creations as players interact with the game world.
The thing is, the original creators used a Slot Map / Object Pool on which pointers are re-used when an object is removed. This is a huge performance boost since there's no need to do much memory reallocation unless needed.
And of course, I'm trying to accomplish that myself, but I've come into one huge problem with my Slot Map:
Here's just a few explanation of how Slot Map works according to a source I found online:
Object class is the base class for every game object, my Slot Map / object Pool is using this Object class to save every allocated object.
Example:
struct TObjectBlock
{
Object Object[36768];
};
The way the slot map works is that, the server first allocates, say, 36768 objects in a list of TObjectBlock and gives them a unique ID ObjectID for each Object which can be re-used in a free object list when the server needs to create a new object.
Example:
Object 1 (ID: 555) is deleted, it's ID 555 is put in a free object ID
list, an Item creation is requested, ID 555 is reused since it's on
the free object list, and there is no need to reallocate another
TObjectBlock in the array for further objects.
My problem: How can I use "Player" "Creature" "Item" "Tile" to support this Slot Map? I don't seem to come up with a solution into this logic problem.
I am using a virtual class to manage all objects:
struct Object
{
uint32_t ObjectID;
int32_t posx;
int32_t posy;
int32_t posz;
};
Then, I'd create the objects themselves:
struct Creature : Object
{
char Name[31];
};
struct Player : Creature
{
};
struct Item : Object
{
uint16_t Attack;
};
struct Tile : Object
{
};
But now if I was to make use of the slot map, I'd have to do something like this:
Object allocatedObject;
allocatedObject.ObjectID = CreateObject(); // Get a free object ID to use
if (allocatedObject.ObjectID != INVALIDOBJECT.ObjectID)
{
Creature* monster = new Creature();
// This doesn't make much sense, since I'd have this creature pointer floating around!
monster.ObjectID = allocatedObject.ObjectID;
}
It pretty much doesn't make much sense to set a whole new object pointer the already allocated object unique ID.
What are my options with this logic?
I believe you have a lot of tangled concepts here, and you need to detangle them to make this work.
First, you are actually defeating the primary purpose of this model. What you showed smells badly of cargo cult programming. You should not be newing objects, at least without overloading, if you are serious about this. You should allocate a single large block of memory for a given object type and draw from that on "allocation" - be it from an overloaded new or creation via a memory manager class. That means you need separate blocks of memory for each object type, not a single "objects" block.
The whole idea is that if you want to avoid allocation-deallocation of actual memory, you need to reuse the memory. To construct an object, you need enough memory to fit it, and your types are not the same length. Only Tile in your example is the same size as Object, so only that could share the same memory (but it shouldn't). None of the other types can be placed in the objects memory because they are longer. You need separate pools for each type.
Second, there should be no bearing of the object ID on how things are stored. There cannot be, once you take the first point into consideration, if the IDs are shared and the memory is not. But it must be pointed out explicitly - the position in a memory block is largely arbitrary and the IDs are not.
Why? Let's say you take object 40, "delete" it, then create a new object 40. Now let's say some buggy part of the program referenced the original ID 40. It goes looking for the original 40, which should error, but instead finds the new 40. You just created an entirely untrackable error. While this can happen with pointers, it is far more likely to happen with IDs, because few systems impose checks on ID usage. A main reason for indirecting access with IDs is to make access safer by making it easy to catch bad usage, so by making IDs reusable, you make them just as unsafe as storing pointers.
The actual model for handling this should look like how the operating system does similar operations (see below the divide for more on that...). That is to say, follow a model like this:
Create some sort of array (like a vector) of the type you want to store - the actual type, not pointers to it. Not Object, which is a generic base, but something like Player.
Size that to the size you expect to need.
Create a stack of size_t (for indexes) and push into it every index in the array. If you created 10 objects, you push 0 1 2 3 4 5 6 7 8 9.
Every time you need an object, pop an index from the stack and use the memory in that cell of the array.
If you run out of indexes, increase the size of the vector and push the newly created indexes.
When you use objects, indirect via the index that was popped.
Essentially, you need a class to manage the memory.
An alternative model would be to directly push pointers into a stack with matching pointer type. There are benefits to that, but it is also harder to debug. The primary benefit to that system is that it can easily be integrated into existing systems; however, most compilers do similar already...
That said, I suggest against this. It seems like a good idea on paper, and on very limited systems it is, but modern operating systems are not "limited systems" by that definition. Virtual memory already resolves the biggest reason to do this, memory fragmentation (which you did not mention). Many compiler allocators will attempt to more or less do what you are trying to do here in the standard library containers by drawing from memory pools, and those are far more manageable to use.
I once implemented a system just like this, but for many good reasons have ditched it in favor of a collection of unordered maps of pointers. I have plans to replace allocators if I discover performance or memory problems associated with this model. This lets me offset the concern of managing memory until testing/optimization, and doesn't require quirky system design at every level to handle abstraction.
When I say "quirky", believe me when I say that there are many more annoyances with the indirection-pool-stack design than I have listed.
I am working on a project that needs to load many objects from a data file and store them in memory. Since I have been told that stack space is rare and larger amounts of data should be on the heap I put everything on the heap. However, my impression is that I overdid it a little bit.
My current design looks like this:
class RoadMap
{
unique_ptr<set<unique_ptr<Node>>> allNodes;
void addNode(unique_ptr<Node> node)
{
this->allNodes->insert(std::move(node));
}
}
int main()
{
unique_ptr<RoadMap> map(new RoadMap());
// open file etc.
for (auto nodeData : nodesInFile)
{
map->addNode(unique_ptr<Node>(new Node(nodeData)));
}
}
From what I understand by now, this creates a lot of overhead because there are many unique pointers involved that I think I do not need. If I understand correctly, it should be sufficient to only have one unique pointer barrier in the "pointer chain". However, I am unsure what the best practice is to do this.
Option 1
class RoadMap
{
unique_ptr<set<Node>> allNodes;
void addNode (Node node)
{
this->allNodes->insert(node);
}
}
int main()
{
RoadMap map;
//open file etc.
for (auto nodeData : nodesInFile)
{
map.addNode(Node(nodeData));
}
}
The advantage of this seems to me that the RoadMap class itself is the only one that needs to take care of heap allocation and does so only once when creating the set.
Option 2
class RoadMap
{
set<Node> allNodes;
void addNode (Node node)
{
this->allNodes.insert(node);
}
}
int main()
{
unique_ptr<RoadMap> map(new RoadMap());
// open file etc.
for (auto nodeData : nodesInFile)
{
map->addNode(Node(nodeData));
}
}
Here the unique pointer is only in the main function meaning that the users of the RoadMap class will need to know that this object can become quite large and should be put on the stack. I don't think that this is an overly nice solution.
Option 3
class RoadMap
{
set<unique_ptr<Node>> allNodes;
void addNode(unique_ptr<Node> node)
{
this->allNodes.insert(std::move(node));
{
}
int main()
{
RoadMap map;
// open file etc.
for (auto nodeData : nodesInFile)
{
map.addNode(unique_ptr<Node>(new Node(nodeData)));
}
}
This solution uses many unique pointers which means that when deleting the RoadMap many destructors and deletes will need to be called. Also the RoadMap caller has to supply a unique_ptr when adding a node meaning that he has to do the heap allocation himself.
Right now, I am favouring option 1 over the others. However, I have only been coding C++ for a comparatively short time and am unsure whether I fully understand the concepts behind memory management which is why I want you to (in)validate my opinion. Am I correct in assuming that option 1 is the best way to do this? Do you have any additional references to best practices for this sort of thing?
Give Node a move constructor and move assignment operator (to make operations on the set cheap), then use a mix of option 1 and 2. std::set will already be heap allocating its contents so you don't need to worry about allocating a RoadMap on the heap. Note the extra std::move inside addNode to allow Nodes to be moved into the set.
class RoadMap
{
set<Node> allNodes;
void addNode (Node node)
{
allNodes.emplace(std::move(node));
}
};
int main()
{
RoadMap map;
// open file etc.
for (const auto& nodeData : nodesInFile)
{
map.addNode(Node(nodeData));
}
}
Each of them are quite different from each other.
I would suggest option 2 for simplicity. But it might be more performance intensive in some operations like sort etc because you would be moving the entire Node and not a pointer to it.
I assume that is not a problem, since you are using set. You can still optimize this by using move semantics on your Node object. With out this you are still using 1 copy per add.
The above issue I mention might have been a problem with vector. Another issue you would have with storing the objects directly is the lack of polymorphism. You cant store subtypes of Node, they would get sliced.
If this is an issue I would suggest option 2. Storing pointers means that moving them is faster, and Polymorphism works.
I see no reason for Option 1 or your original solution.
p.s. the this-> in your code is unnecessary.
p.p.s As DyP points out set uses heap anyway, which is what makes Option 2 good. Clue - Stack based structures cannot grow. => Only std::array is I believe stored on stack.
Let me talk a little about the meta problem: You don't want the stack to overflow and hence put your data structures on the heap. That's the right thing to do. But the important thing to understand here is when things will be put onto the heap.
Every local variable is allocated on the stack. If you have data structures of dynamic size, then they refer to the heap in (allmost) all cases. (The only exception I know is when you reserve memory on the stack on purpose with alloca() or std::get_temporary_buffer() or something like it). In particular all STL containers keep their memory on the heap and hardly any stack memory for local variables or member variables is used (except std::array whose size is known at compile-time).
Hence wrapping dynamically sized data structures into unique_ptrs has very little effect, if you want to save stack memory, but it adds indirection to your program which complicates your code, slows down execution and increases heap memory usage unnecessarily.
Here's an example: On Visual Studio 2010 with 32-bit compilation an std::set will use 20 bytes of memory on the stack independent of the template type parameter and of the actual number elements contained in the set. The memory for the set elements is on the heap.
I believe, that you can now make your own decision on whether to use unique_ptrs for the purpose you intent.
Basically it also depends how you want to access the stored Node instances inside your RoadMap instance. I assume your Node instance will release the wrapped note data.
I would go for an adjusted version 2.
I made a cute generic (i.e. template) List class to handle lists in C++. The reason for that is that I found the std::list class terribly ugly for everyday use and since I constantly use lists, I needed a new one. The major improvement is that with my class, I can use [] to get items from it. Also, still to be implemented is an IComparer system to sort things.
I'm using this List class in OBJLoader, my class that loads Wavefront .obj files and converts them to meshes. OBJLoader contains lists of pointers to the following "types": 3D positions, 3D normals, uv texture coordinates, vertices, faces and meshes. The vertices list has objects that must be linked to some objects in all of the 3D positions, 3D normals and uv texture coordinates lists. Faces link to vertices and meshes link to faces. So they are all inter-connected.
For the sake of simplicity, let's consider that, in some context, there are just two lists of pointers: List<Person*> and List<Place*>. Person class contains, among others, the field List<Place*> placesVisited and the Place class contains the field List<Person*> peopleThatVisited. So we have the structure:
class Person
{
...
public:
Place* placeVisited;
...
};
class Place
{
...
public:
List<People*> peopleThatVisited;
};
Now we have the following code:
Person* psn1 = new Person();
Person* psn2 = new Person();
Place* plc1 = new Place();
Place* plc2 = new Place();
Place* plc2 = new Place();
// make some links between them here:
psn1->placesVisited.Add(plc1, plc2);
psn2->placesVisited.Add(plc2, plc3);
// add the links to the places as well
plc1->peopleThatVisited.Add(psn1);
plc2->peopleThatVisited.Add(psn1, psn2);
plc3->peopleThatVisited.Add(plc3);
// to make things worse:
List<Person*> allThePeopleAvailable;
allThePeopleAvailable.Add(psn1);
allThePeopleAvailable.Add(psn2);
List<Place*> allThePlacesAvailable;
allThePlacesAvailable.Add(plc1);
allThePlacesAvailable.Add(plc2);
allThePlacesAvailable.Add(plc3);
All done. What happens when we reach }? All the dtors are called and the program crashes because it tries to delete things two or more times.
The dtor of my list looks like this:
~List(void)
{
cursor = begin;
cursorPos = 0;
while(cursorPos < capacity - 1)
{
cursor = cursor->next;
cursorPos++;
delete cursor->prev;
}
delete cursor;
}
where Elem is:
struct Elem
{
public:
Elem* prev;
T value;
Elem* next;
};
and T is the generic List type.
Which brings us back to the question: What ways are there to safely delete my List classes? The elements inside may or may not be pointers and, if they are pointers, I would like to be able, when I delete my List, to specify whether I want to delete the elements inside or just the Elem wrappers around them.
Smart pointers could be an answer, but that would mean that I can't have a List<bubuType*>, but just List<smart_pointer_to_bubuType>. This could be ok, but again: declaring a List<bubuType*> would cause no error or warning and in some cases the smart pointers would cause some problems in the implementation: for example, I might want to declare a List<PSTR> for some WinAPI returns. I think getting those PSTR inside smart pointers would be an ugly job. Thus, the solution I'm looking for I think should be somehow related to the deallocation system of the List template.
Any ideas?
Without even looking at your code, I say: scrap it!
C++ has a list class template that's about as efficient as it gets, well-known to all C++ programmers, and comes bug-free with your compiler.
Learn to use the STL.1 Coming from other OO languages, the STL might seem strange, but there's an underlying reason for its strangeness, an alien beauty combining abstraction and performance - something considered impossible before Stepanov came and thought up the STL.
Rest assured that you are not the only one struggling with understanding the STL. When it came upon us, we all struggled to grasp its concepts, to learn its particularities, to understand how it ticks. The STL is a strange beast, but then it manages to combine two goals everybody thought could never be combined, so it's allowed to seem unfamiliar at first.
I bet writing your own linked list class used to be the second most popular indoor sport of C++ programmers - right after writing your own string class. Those of us who had been programming C++ 15 years ago nowadays enjoy ripping out those bug-ridden, inefficient, strange, and unknown string, list, and dictionary classes rotting in old code, and replacing it with something that is very efficient, well-known, and bug-free. Starting your own list class (other than for educational purposes) has to be one of the worst heresies.
If you program in C++, get used to one of the mightiest tools in its box as soon as possible.
1Note that the term "STL" names that part of the C++ standard library that stems from Stepanov's library (plus things like std::string which got an STL interface attached as an afterthought), not the whole standard library.
The best answer is that you must think on the lifetime of each one of the objects, and the responsibility of managing that lifetime.
In particular, in a relation from people and the sites they have visited, most probably neither of them should be naturally made responsible for the lifetime of the others: people can live independently from the sites that they have visited, and places exists regardless of whether they have been visited. This seems to hint that the lifetime of both people and sites is unrelated to the others, and that the pointers held are not related to resource management, but are rather references (not in the C++ sense).
Once you know who is responsible for managing the resource, that should be the code that should delete (or better hold the resource in a container or suitable smart pointer if it needs to be dynamically allocated), and you must ensure that the deletion does not happen before the other objects that refer to the same elements finish with them.
If at the end of the day, in your design ownership is not clear, you can fall back to using shared_ptr (either boost or std) being careful not to create circular dependencies that would produce memory leaks. Again to use the shared_ptrs correctly you have to go back and think, think on the lifetime of the objects...
Always, always use smart pointers if you are responsible for deallocating that memory. Do not ever use raw pointers unless you know that you're not responsible for deleting that memory. For WinAPI returns, wrap them into smart pointers. Of course, a list of raw pointers is not an error, because you may wish to have a list of objects whose memory you do not own. But avoiding smart pointers is most assuredly not a solution to any problem, because they're a completely essential tool.
And just use the Standard list. That's what it's for.
In the lines:
// make some links between them here:
psn1->placesVisited.Add(plc1, plc2);
psn2->placesVisited.Add(plc2, plc3);
// add the links to the places as well
plc1->peopleThatVisited.Add(psn1);
plc2->peopleThatVisited.Add(psn1, psn2);
plc3->peopleThatVisited.Add(plc3);
You have instances on the heap that contain pointers to each others. Not only one, but adding the same Place pointer to more than one person will also cause the problem (deleting more than one time the same object in memory).
Telling you to learn STL or to use shared_ptr (Boost) can be a good advice, however, as David Rodríguez said, you need to think of the lifetime of the objects. In other words, you need to redesign this scenario so that the objects do not contain pointers to each other.
Example: Is it really necessary to use pointers? - in this case, and if you require STL lists or vectors, you need to use shared_ptr, but again, if the objects make reference to each other, not even the best shared_ptr implementation will make it.
If this relationship between places and persons is required, design a class or use a container that will carry the references to each other instead of having the persons and places point at each other. Like a many-to-many table in a RDBMS. Then you will have a class/container that will take care of deleting the pointers at the end of the process. This way, no relations between Places and Persons will exist, only in the container.
Regards, J. Rivero
Without looking the exact code of the Add function, and the destructor of your list, it's hard to pin point the problem.
However, as said in the comments, the main problem with this code is that you don't use std::list or std::vector. There are proven efficient implementations, which fit what you need.
First of all, I would certainly use the STL (standard template library), but, I see that you are learning C++, so as an exercise it can be nice to write such a thing as a List template.
First of all, you should never expose data members (e.g. placeVisited and peopleThatVisited). This is a golden rule in object oriented programming. You should use getter and setter methods for that.
Regarding the problem around double deletion: the only solution is having a wrapper class around your pointers, that keeps track of outstanding references. Have a look at the boost::shared_ptr. (Boost is another magnificent well-crafted C++ library).
The program crashes because delete deallocates the memory of the pointer it is given it isn't removing the items from you list. You have the same pointer in at least two lists, so delete is getting called on the same block of memory multiple times this causes the crash.
First, smart pointers are not the answer here. All they will do is
guarantee that the objects never get deleted (since a double linked list
contains cycles, by definition).
Second, there's no way you can pass an argument to the destructor
telling it to delete contained pointers: this would have to be done
either via the list's type, one of its template arguments, partial
specialization or an argument to the constructor. (The latter would
probably require partial specialization as well, to avoid trying to
delete a non-pointer.)
Finally, the way to not delete an object twice is to not call delete on
it twice. I'm not quite sure what you thing is happening in your
destructor, but you never change cursor, so every time through, you're
deleting the same two elements. You probably need something more along
the lines of:
while ( cursor not at end ) {
Elem* next = cursor->next;
delete cursor;
cursor = next;
}
--
James Kanze
And you could easily implement [] around a normal list:
template <class Type>
class mystdlist : public std::list<Type> {
public:
Type& operator[](int index) {
list<Type>::iterator iter = this.begin();
for ( int i = 0; i < index; i++ ) {
iter++;
}
return *iter;
}
};
Why you would want to do this is strange, IMHO. If you want O(1) access, use a vector.