API design choice pointer vs. object

D

Divick

Hi,
I am designing an API and the problem that I have is more of a
design issue. In my API say I have a class A and B, as shown below

class A{
public:
void doSomethingWithB( B * b)
{
//do something with b
//possibly store in a list
listB.push_back(b);
}
private:
std::vector<B *> listB;
};

class B
{
///some declarations and /or data members
};

Now the problem is either the interface to doSomethingWithB could be

void doSomethingWithB( B * b)
or
void doSomethingWithB( B & b)

If I use pointers they come with all the mess as then I might have to
do some sort of reference counting on them to tell somebody that I am
storing the pointer to B and possibly call delete or unref on them when
I remove them from the list if the ref count goes to 0. This adds lots
of memory management overhead /code to the piece above.

If I use reference rather then pointers, then probably I am not giving
the user of the API the full flexibility. I am not very sure that what
pros /cons will it have if I don't provide a interface with pointers.

Frankly until now I almost used to use pointers everywhere for member
objects, for method parameters, method return type. And this I feel is
bad.

If people can provide reasons for/against the pointer/reference
interface then it would help a lot.

Thanks,
Divick
 
V

Victor Bazarov

Divick said:
[..]
If people can provide reasons for/against the pointer/reference
interface then it would help a lot.

Can you say, "Google"? "Pointer versus reference" discussion is such
a dead horse that I can barely see the tracks in the dust where the whip
hit it last time we were beating it.

V
 
N

Noah Roberts

Divick said:
Hi,
I am designing an API and the problem that I have is more of a
design issue. In my API say I have a class A and B, as shown below

class A{
public:
void doSomethingWithB( B * b)
{
//do something with b
//possibly store in a list
listB.push_back(b);
}
private:
std::vector<B *> listB;
};

class B
{
///some declarations and /or data members
};

Now the problem is either the interface to doSomethingWithB could be

void doSomethingWithB( B * b)
or
void doSomethingWithB( B & b)

Is B polymorphic? If yes you have your answer, it must be the former.

Do you want to keep copies of B or the actual B itself? If copies then
it can be the later but otherwise must be former.

For reference counting and such if you have the option you should use
Boost's shared_ptr. If not then create one...it isn't too tough.
 
M

Marcus Kwok

Noah Roberts said:
Is B polymorphic? If yes you have your answer, it must be the former.

I think polymorphism works with references too:


#include <iostream>

class A {
public:
virtual ~A() { }
virtual void do_something() { std::cout << "A::do_something()\n"; }
};

class B : public A {
public:
virtual void do_something() { std::cout << "B::do_something()\n"; }
};

void do_it(A& a)
{
a.do_something();
}

int main()
{
B b;
do_it(b);

return 0;
}


Output:
B::do_something()
 
A

Axter

Noah said:
Is B polymorphic? If yes you have your answer, it must be the former.

That should not be a factor in making a determiniation.
If B is polymorphic, it will work with pointers or reference type.


Author's like Herb Sutter, recommend preferring to use reference type
over pointer type, because a pointer can mean two things, and therefore
more ambiguous.

I recommend a reference type, unless you know for sure you need a
pointer.
 
M

Mirek Fidler

void doSomethingWithB( B * b)
or
void doSomethingWithB( B & b)

If I use pointers they come with all the mess as then I might have to
do some sort of reference counting on them to tell somebody that I am
storing the pointer to B and possibly call delete or unref on them when
I remove them from the list if the ref count goes to 0. This adds lots
of memory management overhead /code to the piece above.

If I use reference rather then pointers, then probably I am not giving
the user of the API the full flexibility. I am not very sure that what
pros /cons will it have if I don't provide a interface with pointers.

....or quite opposite. In fact, when designed right, you can gain much
more flexibility without pointers and passing ownership (actually, this
is about ownership rahter than anything else). See

http://upp.sourceforge.net/www$uppweb$overview$en-us.html

(I think the "Who owns widgets" section is the most relevant here).

Mirek
 
D

Daniel T.

"Divick said:
Hi,
I am designing an API and the problem that I have is more of a
design issue. In my API say I have a class A and B, as shown below

class A{
public:
void doSomethingWithB( B * b)
{
//do something with b
//possibly store in a list
listB.push_back(b);
}
private:
std::vector<B *> listB;
};

class B
{
///some declarations and /or data members
};

Now the problem is either the interface to doSomethingWithB could be

void doSomethingWithB( B * b)
or
void doSomethingWithB( B & b)

If people can provide reasons for/against the pointer/reference
interface then it would help a lot.

In this particular case, because of the possibility of A storing the
object beyond the return of the function, I would use a pointer.
 
D

Divick

..or quite opposite. In fact, when designed right, you can gain muchI did not really get the point that this page is referring to. Could
you please explain what this page tries to explain.

Thanks,
Divick
 
D

Divick

In this particular case, because of the possibility of A storing theWhat is the reason behind? Could you please elaborate this more.

Thanks,
Divick
 
B

Ben Pope

Divick said:
I did not really get the point that this page is referring to. Could
you please explain what this page tries to explain.

It's probably referring to some lifetime management issues.

You should have a clear policy of how objects are allocated and how
objects are deleted.

If you are pushing "random" pointers into a list, who manages the
lifetime of the object to which it points? Is it the list? So that all
accesses are done via the list, and if it is removed from the list it is
destroyed? Is it some other entity? Will that other enitity remove it
from the list?

You can use a smart pointer to do the reference counting for you, in
which case I suggest boost::shared_ptr.

Ben Pope
 
N

Noah Roberts

Marcus said:
I think polymorphism works with references too:


#include <iostream>

class A {
public:
virtual ~A() { }
virtual void do_something() { std::cout << "A::do_something()\n"; }
};

class B : public A {
public:
virtual void do_something() { std::cout << "B::do_something()\n"; }
};

void do_it(A& a)
{
a.do_something();
}

int main()
{
B b;
do_it(b);

return 0;
}


Output:
B::do_something()

That's nice, now go look at the OP's question again. Both replies to
my answer missed an important detail about the OP's code.
 
M

Mirek Fidler

Ben said:
It's probably referring to some lifetime management issues.

Bingo! ;)
You can use a smart pointer to do the reference counting for you, in
which case I suggest boost::shared_ptr.

Or you can design your code so that it does majority of lifetime
managemenent by scope destructors.

Mirek
 
M

Marcus Kwok

Noah Roberts said:
That's nice, now go look at the OP's question again. Both replies to
my answer missed an important detail about the OP's code.

OK, I re-read the OP's question, and I'm sorry but I still don't see
what this "important detail" is.


#include <iostream>
#include <vector>

class B {
public:
virtual ~B() { }
virtual void blah() { std::cout << "B::blah()\n"; }
};

class C : public B {
public:
virtual void blah() { std::cout << "C::blah()\n"; }
};

class A {
public:
void doSomethingWithB(B* b)
{
listB.push_back(b);
b->blah();
}

void doSomethingWithB(B& b)
{
listB.push_back(&b);
b.blah();
}

private:
std::vector<B *> listB;
};


int main()
{
A a;
B b;
C c;
a.doSomethingWithB(&b);
a.doSomethingWithB(&c);

a.doSomethingWithB(b);
a.doSomethingWithB(c);
}


Output:
B::blah()
C::blah()
B::blah()
C::blah()
 
M

Marcus Kwok

Marcus Kwok said:
OK, I re-read the OP's question, and I'm sorry but I still don't see
what this "important detail" is.

Sorry for following up to myself, but upon thinking, maybe you meant the
part about storing them in a vector and retaining polymorphic behavior.


#include <iostream>
#include <vector>

class B {
public:
virtual ~B() { }
virtual void blah() { std::cout << "B::blah()\n"; }
};

class C : public B {
public:
virtual void blah() { std::cout << "C::blah()\n"; }
};

class A {
public:
void doSomethingWithB(B* b)
{
listB.push_back(b);
b->blah();
}

void doSomethingWithB(B& b)
{
listB.push_back(&b);
b.blah();
}

void doSomethingWithAllB()
{
std::cout << "ALL:\n";
typedef std::vector<B*>::const_iterator CI;
for (CI i = listB.begin(); i != listB.end(); ++i) {
(*i)->blah();
}
}

private:
std::vector<B*> listB;
};


int main()
{
A a;
B b;
C c;
a.doSomethingWithB(&b);
a.doSomethingWithB(&c);

std::cout << '\n';

a.doSomethingWithB(b);
a.doSomethingWithB(c);

std::cout << '\n';

a.doSomethingWithAllB();
}


Output:
B::blah()
C::blah()

B::blah()
C::blah()

ALL:
B::blah()
C::blah()
B::blah()
C::blah()
 
D

Daniel T.

What is the reason behind? Could you please elaborate this more.[/QUOTE]

1) A holds pointers to B's so it makes sense to accept pointers in the
member-function that receives the B's.

2) The primary reason references were added to the language was to
facilitate operator overloading. In those functions, the object is never
stored beyond the end of the function. I tend to follow suit.
 
J

Jeremy Jurksztowicz

I would suggest you avoid manually managing scope where possible. Even
a simple boost::scoped_ptr is preferable. Also, if lifetime tracking is
an issue, then take a good long look at boost::weak_ptr. The
shared_ptr/weak_ptr combo has become an integral part of my run-time
polymorphic class design, it just saves so many headaches.

But, as suggested above, first try and analyse the lifetime of B's if
possible. In library design lifetime analysis is often difficult
because you need to make some assumptions about how your library will
be used.

Good Luck!
Jeremy Jurksztowicz
 
D

Divick

I would suggest you avoid manually managing scope where possible. Even
I have several questions with respect to smart pointers, since you and
others as well have advised to use them.

1. Is shared_ptr and other smart pointers thread safe? I am planning to
use them in a threaded and multiprocessor environment.
2. I don't want to expose to the client programmer that the return type
of some method is a boost::smart_ptr because the client programmer
might not be conversant with the smart pointers and I do not want him
to delve into how I handle the pointes internally in my API. If I do a
typedef the types like typedef HANDLE boost::shared_ptr<MyClass> then I
will need to document that the client programmer can use that HANDLE as
he would use pointer to MyClass, but it might confuse him altogether.
So the question really is "Is it a good idea to expose to the end user
the boost types as return values?"
3. Since boost pointers require the template class passed should have
the destructor as public, but what if I need to have a class with
protected destructor?

As far as managing lifetimes are concerned, I understand that passing
as reference is fine in the method parameters if the function is not
going to store the object. Since references to objects on stack
automatically get deleted due to scope, there is no problem or
ambiguity.

The problem really comes when you need to return pointers or objects
from functions and when you need to store the pointers/objects. My API
needs to return objects/pointers to the client programmer of some
objects created by my Factory classes. Since I cannot return reference
to an object created on the stack (as it will get deleted as soon as
the function returns), I will need to call new then either I can return
the pointer or I can return the reference like this:

A& createObjA()
{
A &b = *(new B()); //B subclasses from A

//Store the b somewhere because the object b is some system
resource
//which I need to keep track of.
code to store object b somewhere
return b;
}
I have rarely seen any API do this i.e. returning a reference to an
object created on heap. Is it considered a bad practice or what?

or
A* createPtrA()
{
A *a = new B();
return a;
}
Most API's use this kind of syntax.
But the problem is that now since I return a pointer then if the same
object needs to be passed to one of the functions of my API then the
programmer will hate to call a function with reference argument like
this:

//Function in my API accepting reference to A
void f(A &a)
//To call such function in my API the client programmer will write:
A * a = createPtrA(); //createPtrA is my API call
f(*a); //f is another function defined in my API

instead he would prefer a cleaner syntax like
A & a = createA(); //createA returns a reference to an object allocated
on heap
f(a); //And f accepts reference to A rather then pointer to A

But again I have never seen any API do any of the above instead they
would simply avoid references altogether and simply use pointers
everywhere i.e. in return types as well as arguments. And I find there
are lots of postings in this newsgroup itself where people say that
"the biggest mistake people make having knowledge of C, possibly
introduces some peoples need to use pointers everywhere". But I see out
there lots and lots of C++ API's just relying on pointers as the only
solution and believe me these API are very well known ones. All this
really leaves me astray and the question that I have is is it really
worth to design API with smart pointers / references rather then using
simple plain pointers?

Any suggestions / comments are welcome,
Thanks a ton for all the posts above,
Divick
 
D

Daniel T.

I have several questions with respect to smart pointers, since you and
others as well have advised to use them.

1. Is shared_ptr and other smart pointers thread safe? I am planning to
use them in a threaded and multiprocessor environment.
2. I don't want to expose to the client programmer that the return type
of some method is a boost::smart_ptr because the client programmer
might not be conversant with the smart pointers and I do not want him
to delve into how I handle the pointes internally in my API. If I do a
typedef the types like typedef HANDLE boost::shared_ptr<MyClass> then I
will need to document that the client programmer can use that HANDLE as
he would use pointer to MyClass, but it might confuse him altogether.
So the question really is "Is it a good idea to expose to the end user
the boost types as return values?"
3. Since boost pointers require the template class passed should have
the destructor as public, but what if I need to have a class with
protected destructor?

As far as managing lifetimes are concerned, I understand that passing
as reference is fine in the method parameters if the function is not
going to store the object. Since references to objects on stack
automatically get deleted due to scope, there is no problem or
ambiguity.

The problem really comes when you need to return pointers or objects
from functions and when you need to store the pointers/objects. My API
needs to return objects/pointers to the client programmer of some
objects created by my Factory classes.[/QUOTE]

As far as managing lifetimes are concerned, anytime you have more than
one pointer/reference pointing/referring to the same object, you have to
worry about when the destructor will be called on that object and make
sure that both holders know when that will be. *EVEN* when you pass a
pointer/reference in a method parameter and you don't plan on storing it
past the end of that method. (In a multi-threaded situation, for
example, the objects delete could be called in the middle of your
method.)

One way to solve the problem is to make sure only one reference/pointer
is connected to every object. As such, I would recommend against you
passing a reference to an object to the client *and* storing it in the
server class. IE:

A& createObjA()
{
A &b = *(new B()); //B subclasses from A

//Store the b somewhere because the object b is some system resource
//which I need to keep track of.
// code to store object b somewhere
return b;
}

Is a bad idea. I will go so far as to say that the only time a function
should return a modifiable reference is if it returns '*this' (or if you
are writing a basic container class.) In all other cases, if returning
something it should return an object, const object&, or object* (either
const or non-const). In the later case, make it clear who is supposed to
delete the actual object and exactly when that will happen. The choices
are:

1) The client must delete it, and can do so any time it likes.
2) The client must *not* delete it. Instead, the server will delete it
when particular functions are called (and in its destructor.)

If the function returns a const object&, then it should be made clear
that the server class can destroy the object by the time any other
member function is called. IE returning by const object& should be
thought of only as an optimization over turning by object.

Given these rules, the below is fine:

A* createPtrA()
{
A *a = new B();
return a;
}

As long as the documentation for the function makes it clear that it is
the caller's responsibility to delete the object (choice 1 above.) Some
go so far as to wrap it in an auto_ptr, as in:

auto_ptr<A> createPtr() {
return auto_ptr<A>( new B );
}
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top