invoking Base::virtualMethod instead of Derived

Started by
23 comments, last by nlbs 15 years, 5 months ago
So you meant I need to do something like.
Object(const Object & r) {  this = r.clone();}Object operator=(const Object & r){  this = r.clone();}

Right ??

If I code like this e.g. `this =` syntax says `lvalue required as left operand of assignment`

I meant I cant understand how to call/use the clone() from copy constructor or operator=()
Advertisement
Quote:Original post by nlbs
So you meant I need to do something like.
*** Source Snippet Removed ***
Right ??

If I code like this e.g. `this =` syntax says `lvalue required as left operand of assignment`

I meant I cant understand how to call/use the clone() from copy constructor or operator=()


You need to add it to JType. Something like this:
#include <iostream>#include <string>#include <map>struct JType {	JType()          { std::cout << "JType()" << std::endl; }	virtual ~JType() { std::cout << "~JType()" << std::endl; }	virtual void commit() = 0;	virtual JType * clone() const = 0;};struct JDefault : public JType {	JDefault()    { std::cout << "JDefault()" << std::endl; }	~JDefault()   { std::cout << "~JDefault()" << std::endl; }	void commit() {	std::cout << "Commit not supported" << std::endl; }	JDefault * clone() const { return new JDefault(*this); }};struct JString : public JType {	JString(const std::string & value = "Hello World")		: s(value)	{ 		std::cout << "JString()" << std::endl; 	}	~JString()    { std::cout << "~JString()" << std::endl; }	void commit() {	std::cout << s << std::endl; }	JString * clone() const  { return new JString(*this); }private:	std::string s;};class Object {	typedef JType * T;	typedef std::map<std::string, T> List;	struct Proxy {		friend class Object;		template < class U >		Proxy & operator=(const U & rhs) {			clear();			ref = new U(rhs);			return *this;		}		bool exists() const { return ref != NULL; }		void clear() { delete ref; ref = NULL; }		T operator->() const { return value(); }		T operator*() const { return value(); }	private:		Proxy(const List::iterator & i) : ref(i->second) {}		Proxy(const Proxy & other) : ref(other.ref) {}		// assignment isn't allowed		Proxy & operator=(const Proxy &) { }		T value() const { 			static JDefault def;			return (ref == NULL) ? &def : ref; 		}		T & ref;	};	struct ManagedMap {		~ManagedMap() {			List::iterator i = list.begin();			for (List::iterator i = list.begin(); i!= list.end(); ++i) delete i->second;				}		List::iterator insert(const std::string & key) {			return list.insert(list.end(), std::make_pair(key, T(NULL)));		}		List list;	};	void swap(ManagedMap & lhs, ManagedMap & rhs) {		std::swap(lhs.list, rhs.list);	}	      List & list()       { return entries.list; }	const List & list() const { return entries.list; }	ManagedMap entries;public:	Object() {}	Object(const Object & other) {		ManagedMap temp;		for  (List::const_iterator i = other.list().begin(); i != other.list().end(); ++i) {			List::iterator curr = temp.insert(i->first);			if (i->second != NULL) curr->second = i->second->clone();		}		swap(entries, temp);	}	Object & operator=(const Object & other) {		Object o(other);		swap(entries, o.entries);		return *this;	}	~Object() {}	Proxy operator[](const std::string & key) {		List::iterator i = list().find(key);		if (i != list().end()) {			return Proxy(i);		} else {			return Proxy(entries.insert(key));		}	}	bool has(const std::string & key) const {		return list().count(key) != 0;	}};int main(int argc, char* argv[]){	Object o;	JString js;	o["a"] = js;	o["math"] = JString();	Object q;	o = o;	q = o;	Object r;	r = q;	r["foo"] = JDefault();	r["bar"] = JString("bar");	o = r;	if (o["empty"].exists()) std::cout << "'empty' should not exist" << std::endl;	o["math"]->commit();	o["math"].clear();	o["math"]->commit();	o["foo"]->commit();	o["bar"]->commit();	o["empty"]->commit();}


A few comments on that:
- I still don't guarantee I covered all the corner cases
- This is why C++ is hard
- To understand why things are implemented in this way, best references are Exceptional C++, More Exceptional C++ and few others.

I'd strongly suggest you look into those sources and examine the finer points of C++ before trying designs like this. The difference between something that compiles and something that works somewhat reliably is simply too big in C++.
would you mind to clarify why you used ManagedMap instead of using simply std::map
why did you use swap instead of copy ??

[Edited by - nlbs on November 10, 2008 1:18:55 AM]
Quote:Original post by nlbs
would you mind to clarify why you used ManagedMap instead of using simply std::map
why did you use swap instead of copy ??


To properly implement memory management and attempt to provide exception safety in cases where Object gets copied.

That is covered in the books I mentioned, as well as various other sources, but it's somewhat extensive topic that I cannot summarize in a few sentences.
But the thing is.
Object o;o["x"] = JString("X");Object p;p["y"] = JString("Y");//Now when you doo = p;

Now p should be copied into o and o should also contain what p contains.
But as you are using swap the contents of p is going to o and contents of o is going to p and obviously its not intented by the term COPY.

This is what I meant to say.
Quote:Original post by nlbs
Now p should be copied into o and o should also contain what p contains.


Maybe that is what you require, but it is definitely not implied by syntax. If you need such operation, then define a merge() function or something - but never assignment operator.

Quote:But as you are using swap the contents of p is going to o and contents of o is going to p and obviously its not intented by the term COPY.


Object o;o["x"] = JString("X");   // o contains xObject p;p["y"] = JString("Y");   // p contains y//Now when you doo = p;                   // o contains y                         // p contains y, x no longer exist


This is exactly what assignment operator does.

Object & operator=(const Object & other) {		Object o(other);           // make a copy		swap(entries, o.entries);  // swap with copy		return *this;              // destroy copy	}

other is not modified.
Thanks for clearing the fact.
Quote:Original post by Antheus
Quote:Original post by nlbs
Now p should be copied into o and o should also contain what p contains.


Maybe that is what you require, but it is definitely not implied by syntax. If you need such operation, then define a merge() function or something - but never assignment operator.


No thats definitely not what I want to do. first I thought that this is what your code does. However you have clarified that just now.

Now another question is. we are not storing raw pointers in Object Class we are storing a std::map container instance which is storing raw pointers.

so why there would be a need to use another ManagedMap..

I meant to say what are the difficulties we would fetch if we use std::map only no ManagedMap.

Thanks again.
Quote:Original post by nlbs

so why there would be a need to use another ManagedMap..

I meant to say what are the difficulties we would fetch if we use std::map only no ManagedMap.


It is needed to define custom destructor that will delete the raw pointers we stored in there. std::map only releases what it allocates.

And since we auto-allocate ManagedMap instances, the compiler makes sure whenever map is deallocated, so are all instances we allocated with new.

It isn't strictly required, but is considerably more elegant. Without that, we'd need to use try/catch block to handle problems that occur during copy construction or assignment.
I found a risk but I dont know how much important it is.
suppose I've a JString* member variable.
and somebody have inserted it into a Local Object Instance which gets auto destructed. (I have no reason to believe that people who uses my class are smart enough)

Now the member variable of JString* Type will also get destructed.

But it was not intended.

should I use a std::map of std::auto_ptr in managed map
and JType -> auto_ptr<JType> abstractions will be provided by ManagedMap itself ?? also I dont need to do explicit delete operation on pointers as I am storing auto_ptr Instances on the map.

would this solve the above mentioned problem ??
would any other problem / risk / corner case would appear ??
Quote:Original post by nlbs
I found a risk but I dont know how much important it is.
suppose I've a JString* member variable.
and somebody have inserted it into a Local Object Instance which gets auto destructed. (I have no reason to believe that people who uses my class are smart enough)


Can you show an example?

Quote:should I use a std::map of std::auto_ptr in managed map


auto_ptr cannot be used with standard library containers.

This topic is closed to new replies.

Advertisement