The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Help with C++ references

Hi,

It's been a while since I've used C++ references. I'm trying to put together a smart pointer design that reference-counts some underlying problem. The idea is that once no smart-pointer refers to the underlying object it is deleted.

The problem I have is that I have this method:

SmartProxy& Dog::getInstance();

and it isn't clear how I can create Foo to be returned. If I do this:

SmartProxy& Dog::getInstance()
{
  return SmartProxy(new Dog());
}

then the compiler complains I am returning a reference to SmartProxy which is allocated on the stack as a local variable. Fair enough, but if I allocate the SmartProxy on the heap as follows:

SmartProxy& Dog::getInstance()
{
  return *(new SmartProxy(new Dog()));
}

then who cleans this up? Will the SmartProxy gets deleted off the heap when the returned variable falls out of scope of the callee? Please help :)

Thanks,
Gili
Gili Send private email
Friday, August 03, 2007
 
 
Some clarifications:

- "Foo" should have read "Dog"
- I am not allowed to use templates or exceptions in my library which is why I am reinventing the wheel here.
Gili Send private email
Friday, August 03, 2007
 
 
Just return it via value not a reference - your proxy has to have proper copy construction and assignment operator mechanisms in place so it will work perfectly.
RnR Send private email
Friday, August 03, 2007
 
 
The standard way to do this is to use a static variable:

SmartProxy& Dog::getInstance()
{
  static SmartProxy theInstance(new Dog);
  return theInstance;
}

The other way to do it is with a static variable in the class, instead of in the function, but that's trickier, particularly if you care about thread safety.  Most of the time I don't, but it came up in an interview once, and the version above doesn't have such problems.
Michael G Send private email
Friday, August 03, 2007
 
 
First and foremost, use boost::shared_ptr - it does exactly what you want: http://www.boost.org/libs/smart_ptr/shared_ptr.htm
You can also look at the source code.

Second, the correct way to implement Dog::getInstance is:

SmartProxy Dog::getInstance()
{
  return SmartProxy(new Dog());
}

That is, return by value, not by reference. The static variable idea is bad for two reasons:
1. The static variable is instantiated only once so it will always return a reference to the same value
2. That value will not be deleted until the program terminates
Dan Shappir Send private email
Friday, August 03, 2007
 
 
Michael,

I can't do it the way you mentioned because the method returning the SmartProxy does not own the underlying object. RnR's approach sounds like a good start, I'll give it a try now.

BTW: No one answered this question yet,

return *(new SmartProxy(new Dog()));

what happens in the above case? Will it always leak memory or will the pointer gets deleted when the callee lets the reference fall out of scope?
Gili Send private email
Friday, August 03, 2007
 
 
Dan, thanks for the reference to shared_ptr!

Guys, I am now returning by value and this seems to work except now I've run into a related problem.

I'm inside the constructor of an object that contains a field "SmartProxy& foo". Now, I'd like to execute the following in the constructor:

foo = SmartProxy(new Dog());

unfortunately the compiler complains:

warning C4239: nonstandard extension used : 'argument' : conversion from 'SmartProxy' to 'SmartProxy &'. A reference that is not to 'const' cannot be bound to a non-lvalue; assigment operator takes a reference to non-const

Any ideas?
Gili Send private email
Friday, August 03, 2007
 
 
The 'BTW' question will leak memory.

Also, may I suggest you change the name of the function so it isn't 'getInstance' - I read through the post somewhat quickly (my bad), and getInstance is the standard name for a singleton, which would then be implemented the way I had suggested.  Obviously, you don't want a singleton, so it's confusing to someone who just scans your code.
Michael G Send private email
Friday, August 03, 2007
 
 
Damnit :) I just ran into a major hurdle. The original code that used pointers relied upon covariant return types. The new code returns by value whenever a SmartProxy is created so now I have:

class Recognizer
{
  GrammarProxy createGrammar();
}

class EmbeddedRecognizer: public Recognizer
{
  EmbeddedGrammarProxy createGrammar();
}

and the compiler complains:

error C2555: 'EmbeddedRecognizer::createGrammar': overriding virtual function return type differs and is not covariant from 'Recognizer::createGrammar'
Gili Send private email
Friday, August 03, 2007
 
 
It seems I have run into the problem described here: http://ciaranm.org/show_post/146

No known workarounds :(
Gili Send private email
Friday, August 03, 2007
 
 
Gili - if you want to initialize a reference member variable you need to do it in the initializer list like this:
class ble
{
public:
ble( ble2& _ble )
: m_ble( _ble )
{
}
ble2& m_ble;
};

This is just like normal references - you have to initialize them in the point of declaration - than all m_myRef = ... statements do the assignment to the referenced object and NOT the reference - besides the declaration point/initializer list you don't have access to the reference itself.

btw - shared_ptr is a template - you said you don't want templates so please decide - if you may use shared_ptr use it.

btw2 - this topic does not have much in common with the "design" of software but rather "c++ basics" ;)

best regards
RnR Send private email
Friday, August 03, 2007
 
 
RnR,

  I cannot initialize the proxy in the initializer list because it wraps the current object and you cannot pass "this" into a method invoked in the initializer list:

Foo::Foo():
proxy(FooProxy(this))
{}

warning C4355: 'this' : used in base member initializer list

  I've worked around this problem by converting the field from FooProxy& to FooProxy. This costs me one extra copy but I don't think there is another way.

  On the topic of covariant returns types I'm seriously considering giving them up for the sake of using this proxy mechanism. I think resource management is more important than type safety in my particular use-case because it is an asynchoronous API and managing resources by hand is a pain. I wish I could have both though :)
Gili Send private email
Friday, August 03, 2007
 
 
Why do you want an object to store a shared ptr to itself? - once you create a "shared_ptr" object you should not create a new family of those "shared_ptr"'s (unless the reference count is done in the object the pointers point too).

Additionally - if you store a proxy to this as a member when will the this object be destroyed? (it will always hold a reference to itself...)
RnR Send private email
Saturday, August 04, 2007
 
 
RnR,

I no longer have Foo contain a field to FooProxy because of the circular dependency problem you mentioned.

You wrote:
>once you create a "shared_ptr" object you should not create
>a new family of those "shared_ptr"'s (unless the reference
>count is done in the object the pointers point too).

In my case the reference-count is done in the object the pointers point to. I'm not sure I can see another way of doing this. I noticed some implementations used a "static map<Object*, ReferenceCount>" to achieve this unfortunately I don't have STL in my platform and as such I don't have a map implementation available to me. I think that in this case moving the reference count into the actual object is the lesser of two evils :) Do you agree?

Gili
Gili Send private email
Sunday, August 05, 2007
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz