Prev: Distinguish between pointers created with 'new' and created with references.
Next: Distinguish between pointers created with 'new' and created with references.
From: Goran on 15 Jul 2010 08:59 On Jul 15, 2:03 pm, dailos <dailos.guerr...(a)gmail.com> wrote: > Hi all, > I am struggling with a design problem. It showed up because of some > architectures constraints. > I have a group of classes which derive from an abstract one. > I have a vector with pointers to the generic abstract class (neither > vector of references nor vector of classes are allowed since it's > abstract). > A member function that loads objects into the vector, lets say from > file, has to do it creating object with new operator since it couldn't > be done by references to objects created locally. > A public functions lets the user to add single objects too but it > could use new or reference as an argument. > The problem is when I need to call the destructor, and erase all of > the objects created within the vector. > I thought of using delete operator for each element in the vector, but > what if some other elements has been added from a reference. delete > &something ## failure. > > I guess there is no way to distinguish between object address created > with new and created with a reference. > How a to write a proper destructor then?? This problem of your crops up here in various forms, but does it regularly. There is no other good solution to your problem but to take care of the lifetime of your objects yourself. This is because C++, just like C, are languages where you manage heap manually. If you browse through here, you might find some "solutions", but they are all non-standard, wrong, of limited use, or a combination thereof. In your main(), lifetime of theOrange is the block ({}) created by main. You don't do anything about it, language runtime destroys it when block is done. Lifetime of objects created in loadFruits is whatever you decide, because these are on the heap, and you have to manage heap manually. You have to delete them once you won't ever try to use them again. Now, as to what could work... You could declare addFruit like this: void addFruit(auto_ptr<Fruit> p); Given what auto_ptr does, it's clear that you can only addFruit allocated on the heap. So you can take pointer out of "p" and put it in Basket, and Basket is the owner of it's fruits (meaning, when Basket is destroyed, all it's fruit is deleted). That's not unreasonable lifetime handling strategy. You could make it more complicated (warning: compiled with head- debugger, debugged with head-debugger and tested with head-test- suite): class FruitHolder { public: FruitHolder(Fruit& f) : _owns(false), _f(&f) {} FruitHolder(Fruit* f) : _owns(true), _f(f) {} FruitHolder() : _owns(false), _f(NULL) {} // standard containers need this FruitFolder& operator=(FruitFolder& rhs) { if (this == &rhs) return rhs; if (_owns) delete _f; _f = rhs._f; if (rhs._owns) { _owns = true; rhs._owns = false; } } ~FruitHolder() { if (_owns) delete f; } operator Fruit*() { return _f; } operator const Fruit*() const { return _f; } private: bool _owns; // Perhaps "mutable" and operator=(const FruitFolder& rhs)? Fruit* _f; FruitHolder(const FruitHolder&); // Not needed and potentially harmful. }; vector<FruitHolder> fruitList; void addFruit(auto_ptr<Fruit> p) { FruitHolder fh(p.release()); fruitList.push_back(f); } void addFruit(Fruit& f) { FriutHolder fh(f); fruitList.push_back(fh); } Idea here is that whatever comes in as a reference is not owned by Basket, all else is owned by basket. Fruit Holder is similar to auto_ptr in a sense that it "moves" Fruit pointers around on assignment (provided that my op= up here works), but it doesn't necessarily delete them (_owns flag). I'd be surprised if there wasn't a template library that does the above already (Loki, perhaps? Boost?). All in all, there you have two ( perhaps working ;-) ) solutions. None of them are generic, but I am adamant that a generic one doesn't exist. Goran. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Thiago A. on 15 Jul 2010 08:59 > I have a group of classes which derive from an abstract one. > I have a vector with pointers to the generic abstract class (neither > vector of references nor vector of classes are allowed since it's > abstract). > A member function that loads objects into the vector, lets say from > file, has to do it creating object with new operator since it couldn't > be done by references to objects created locally. > A public functions lets the user to add single objects too but it > could use new or reference as an argument. > The problem is when I need to call the destructor, and erase all of > the objects created within the vector. I did myself the same question when I was making a "Dialog/Form" class and I could have static or dynamic buttons inside this form. Suggestions: 1) Use only dynamic objects (always using new, smart pointers) 2) Use a pair<bool, Object*> do say when the object needs to be deleted. You can create the "true" pair with Add(object*) and the "false" pair with Add(Object&). 3) Create a separate list/concept for the heap objects. The solution 2 is not perfect and it depends of the Add rule convention. The solution 1 and 3 are not always desirable as well. ---- http://www.thradams.com/ -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: red floyd on 16 Jul 2010 01:18 On Jul 15, 5:03 am, dailos <dailos.guerr...(a)gmail.com> wrote: > Hi all, > I am struggling with a design problem. It showed up because of some > architectures constraints. > I have a group of classes which derive from an abstract one. > I have a vector with pointers to the generic abstract class (neither > vector of references nor vector of classes are allowed since it's > abstract). > A member function that loads objects into the vector, lets say from > file, has to do it creating object with new operator since it couldn't > be done by references to objects created locally. > A public functions lets the user to add single objects too but it > could use new or reference as an argument. > The problem is when I need to call the destructor, and erase all of > the objects created within the vector. > I thought of using delete operator for each element in the vector, but > what if some other elements has been added from a reference. delete > &something ## failure. > > I guess there is no way to distinguish between object address created > with new and created with a reference. > How a to write a proper destructor then?? Don't store pointers. Or else only store dynamically allocated pointers. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: MaksimKneller on 16 Jul 2010 01:19
On Jul 15, 8:03 am, dailos <dailos.guerr...(a)gmail.com> wrote: > I guess there is no way to distinguish between object address created > with new and created with a reference. > How a to write a proper destructor then?? 1. a container of references can't be created, only containers of pointers, so that makes your problem easier 2. yes you will need to delete the vector of fruit pointers to avoid mem leak 3. make Fruit base class have a virtual destructor, that way when it goes out of scope the destruct logic will propagate to each derived class 4. make Basket destructor delete the mem of each Fruit pointer, then clear the vector itself 5. FYI, using Boost shared_ptr instead to hold the Fruit pointers will help avoid mem leak if exception is thrown after creating the container and before calling deletes Example code below: class Fruit { public: virtual string name() = 0; virtual ~Fruit() {}; }; class Orange : public Fruit { public: virtual string name(){return string("Orange");}; ~Orange() { cout << "Deleting Orange\n"; } }; class Watermelon : public Fruit { public: virtual string name(){return string("Watermelon");}; ~Watermelon() { cout << "Deleting Watermelon\n"; } }; class Apple : public Fruit { public: virtual string name(){return string("Apple");}; ~Apple() { cout << "Deleting Apple\n"; }; }; class Basket { public: ~Basket() { cout << "Bakset Destructor\n"; // this loop can be avoided if you use Boost's shared_ptr to hold the elements instead of regular // pointers as they will auto delete themselves, but deleting these pointers still needs to be // done either way. for(vector<Fruit*>::iterator i = fruitList.begin(); i != fruitList.end(); i++) { delete *i; } fruitList.clear(); // without this the size will still be 3 cout << fruitList.size(); } void addFruit(Fruit* pFruit); private: vector<Fruit*> fruitList; }; void Basket::addFruit(Fruit* pFruit) { fruitList.push_back(pFruit); } int main() { Basket myBasket; myBasket.addFruit(new Watermelon()); myBasket.addFruit(new Orange()); myBasket.addFruit(new Apple()); return 0; } ----------------------------- Bakset Destructor Deleting Watermelon Deleting Orange Deleting Apple 0 -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |