Question on constness

G

Gonçalo Rodrigues

Hi all,

I have a class, call it Object of heap-allocated objects. They are
managed via a smart pointer class Ref (actually a template class but
that does not matter for the problem). Object instances are created
via a factory method that returns a Ref and all constructors are
protected.

My "problem" is the following. The Ref class is basically a pointer,
e.g. something like

class Ref {
public:
//... some methods.

private:
Object* ptr;
}

and it supports all the usual operations for smart pointers like *,
->, assignment -- is particular ptr is *not* const.

The constructor is, basically

(A)

Ref(Object* ptr) : ptr(ptr) {
//Do some stuff if this->ptr != 0.
}

Now, looking at the signature of the constructor, my first temptation
was to write

(B)

Ref(const Object* ptr) : ptr(ptr) {
//Do some stuff if this->ptr != 0.
}

Because in the "do some stuff" part I do *not* change in any way the
pointed to Object. But, of course, the compiler complains because I'm
assigning a const to a non const. So I was tempted to do

(C)

Ref(const Object* ptr) : ptr(const_cast<Object*>(ptr)) {
//Do some stuff if this->ptr != 0.
}

but that const_cast sure does leave me nervous.

The reason I want to make the signature of the Ref constructor as in
(B) is because a lot of methods create Ref's to their arguments but
otherwise do *not* change them, so, it seems to me, I should declare
them const but because of (A) I cannot. Any advice on this? Is (C) the
best solution? Am I failing to understand the meaning of const?

Best regards,
G. Rodrigues
 
A

Andre Kostur

Hi all,

I have a class, call it Object of heap-allocated objects. They are
managed via a smart pointer class Ref (actually a template class but
that does not matter for the problem). Object instances are created
via a factory method that returns a Ref and all constructors are
protected.

My "problem" is the following. The Ref class is basically a pointer,
e.g. something like

class Ref {
public:
//... some methods.

private:
Object* ptr;
}

and it supports all the usual operations for smart pointers like *,
->, assignment -- is particular ptr is *not* const.

The constructor is, basically

(A)

Ref(Object* ptr) : ptr(ptr) {
//Do some stuff if this->ptr != 0.
}

Now, looking at the signature of the constructor, my first temptation
was to write

(B)

Ref(const Object* ptr) : ptr(ptr) {
//Do some stuff if this->ptr != 0.
}

Because in the "do some stuff" part I do *not* change in any way the
pointed to Object. But, of course, the compiler complains because I'm
assigning a const to a non const. So I was tempted to do

In a certain way of looking at it, you do. OK, you don't modify Object
within this function, but you do allow for some other function to modify
Object via the member variable ptr. The only way to make the argument a
const pointer would be to also make the member variable a const pointer.
If you can get a non-const pointer out of your Ref class (via operator*,
or operator-> for example), then you can't make your constructor
parameter non-const either. If you did, you'd be lying to the user of
the Ref class (semantically speaking). By making the parameter const,
you're promising to the user that the Ref class will not modify the
pointed-to Object, nor will it allow other users of the Ref class to
modify it either.
(C)

Ref(const Object* ptr) : ptr(const_cast<Object*>(ptr)) {
//Do some stuff if this->ptr != 0.
}

but that const_cast sure does leave me nervous.

The reason I want to make the signature of the Ref constructor as in
(B) is because a lot of methods create Ref's to their arguments but
otherwise do *not* change them, so, it seems to me, I should declare
them const but because of (A) I cannot. Any advice on this? Is (C) the
best solution? Am I failing to understand the meaning of const?

Sounds more like you're not entirely understanding your own design. If
you do B, then your Ref class should never have a way to get the non-
const pointer out of Ref. If you could, you could then write code such
as (assuming Ref were constructable without the factory...):

{
const Object abc;
Ref refobj(&abc);

*refobj = 45; // Undefined Behavior!!!
}

At that assignment, you'd be allowing a caller to modify a const object
via a non-const pointer. This is Undefined Behaviour and is "illegal".
 
A

Alf P. Steinbach

* Gonçalo Rodrigues:
I have a class, call it Object of heap-allocated objects. They are
managed via a smart pointer class Ref (actually a template class but
that does not matter for the problem). Object instances are created
via a factory method that returns a Ref and all constructors are
protected.
[snip]

The reason I want to make the signature of the Ref constructor as in
(B) is because a lot of methods create Ref's to their arguments

Deal in Ref's only: if you mix in raw pointers you're asking for trouble.

but
otherwise do *not* change them, so, it seems to me, I should declare
them const but because of (A) I cannot. Any advice on this? Is (C) the
best solution? Am I failing to understand the meaning of const?

One solution is to let 'const' for a Ref mean 'const' for the referred
object.

class Ref
{
...
public:
Object* operator->() { return myPointer; }
Object const* operator->() const { return myPointer; }
};

If Ref is assignable then that may (just may) be impractical, because you
may then want to be able assign a reference to a const Object.

In that case, consider having two classes: Ref and ConstRef, where Ref
converts to ConstRef but not the other way. Then 'const' applies only to the
smartpointer itself, not the referred object.
 
A

aleko

Object instances are created via a factory method that returns a Ref
and all
constructors are protected.

Your object factory returns Refs? Smart pointers are supposed to be
local, i.e.

void myFunc()
{
Ref<MyClass> ptr( getMyClassFactory()->create("myClass") );
ptr->doSomething();

// when the function returns the ptr destructor automatically
// releases the MyClass instance.
}

If your factory returns a dynamically allocated smart pointer, that
defeats the point of using smart pointers.

-Aleko
 
G

Gonçalo Rodrigues

Your object factory returns Refs? Smart pointers are supposed to be
local, i.e.

void myFunc()
{
Ref<MyClass> ptr( getMyClassFactory()->create("myClass") );
ptr->doSomething();

// when the function returns the ptr destructor automatically
// releases the MyClass instance.
}

If your factory returns a dynamically allocated smart pointer, that
defeats the point of using smart pointers.

I'm not sure of the correct terminology (I'm a relative newbie in
C++), but they are smart pointers with shared semantics. An instance
is destroyed when there are no more Ref's pointing to it. The Ref's
themselves are not dynamically allocated but passed and copied around.

Best regards,
G. Rodrigues
 
G

Gonçalo Rodrigues

In a certain way of looking at it, you do. OK, you don't modify Object
within this function, but you do allow for some other function to modify
Object via the member variable ptr. The only way to make the argument a
const pointer would be to also make the member variable a const pointer.
If you can get a non-const pointer out of your Ref class (via operator*,
or operator-> for example), then you can't make your constructor
parameter non-const either. If you did, you'd be lying to the user of
the Ref class (semantically speaking). By making the parameter const,
you're promising to the user that the Ref class will not modify the
pointed-to Object, nor will it allow other users of the Ref class to
modify it either.

Thanks, I was not aware of this.
Sounds more like you're not entirely understanding your own design. If
you do B, then your Ref class should never have a way to get the non-
const pointer out of Ref. If you could, you could then write code such
as (assuming Ref were constructable without the factory...):

{
const Object abc;
Ref refobj(&abc);

*refobj = 45; // Undefined Behavior!!!
}

At that assignment, you'd be allowing a caller to modify a const object
via a non-const pointer. This is Undefined Behaviour and is "illegal".

Constructors are protected, so you never can instantiate a local
object and create a pointer to it - unless some derived class does it.
But hey, the code is under my control and has explicit comments: don't
do that or the whole Universe will collapse.

With your and A. Steinbach's input I'm trying to better my design.

Best regards,
G. Rodrigues
 
A

aleko

An instance is destroyed when there are no more Ref's pointing to it.
The Ref's themselves are not dynamically allocated but passed and copied
around.

Are you using reference counting in your Ref class to control the
lifetime of the objects created by your object factory? Sort of like
COM's AddRef/Release?
If so, that's fine for local objects, but I don't see how you can pass
these outside the function.

Ref<MyClass> &createObject() {
Ref<MyClass> ptr( getObjectFactory()->create("myClass");
return ptr; // wrong, ptr gets destroyed when you leave this
function
}

Hope this helps,
Aleko
 
P

Piotr Filip Mieszkowski

Well, I don't understand something. Why do you return a reference to a
local object? I would do something like this:

Ref<MyClass> getAnObject()
{
Ref<MyClass> ref(getFactory()->getObject());
return ref;
}

The copy constructor of Ref would be called and the data would not be
lost. At least, it works with std::auto_ptr.

Regards,
Piotr Filip Mieszkowski
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top